首页 > 其他分享 >UVA11022 String Factoring

UVA11022 String Factoring

时间:2023-01-22 20:57:13浏览次数:60  
标签:String int Factoring UVA11022 vis str fail operatorname 85

简要题意

给出一个字符串 \(S\),你可以进行任意次(压缩)操作,每次操作可以把字符串中连续几个相同的部分压缩成相同的一个。操作可以嵌套进行。你需要求出操作后字符串的最小长度。

多组数据。以 * 结束。

\(1 \leq |S| \leq 80\),\(S\) 中仅包含英文大写字母。

思路

这道题直接用 KMP 不太好做,我们发现一个区间的答案可以由它的子区间合并得到。于是我们可以考虑区间 DP。

令 \(f_{i,j}\) 为区间 \([i,j]\) 的答案。

显然有 \(f_{i,i}=1\)。

则对于一个不由几个相同部分组成的区间可以枚举断点简单转移得到:

\[f_{i,j}=\min_{k=i}^{j-1}{f_{i,k}+f_{k+1,j}} \]

然后考虑可以压缩的区间。除了上述转移外还有额外的转移。我们令最小重复部分长度(压缩后的长度)为 \(L\)。则额外转移为:

\[f_{i,j}=\min(f_{i,j},f_{i,j+L-1}) \]

然后考虑如何求 \(L\)。我们先求出 \([i,j]\) 之间的字符串的失配数组 \(\operatorname{fail}\)。如果 \((j-i+1) \bmod ((j-i+1)-\operatorname{fail}({j})) = 0\) 则代表可以压缩。然后 \((j-i+1)-\operatorname{fail}({j})\) 就是 \(L\) 了。至于严谨推导过程,见 OI Wiki。时间复杂度为 \(O(n)\)。

然后这道题我们就可以做到 \(O(n^3)\) 了。(据说暴力求 \(L\) 也可以过)

最后贴一下完整的转移方程(如果 LaTex 炸了可以去上面的链接里查看):

\[f_{i,j}=\begin{cases} &1&i=j\\ &\min(\min_{k=i}^{j-1}{(f_{i,k}+f_{k+1,j})},\begin{cases} &f_{i,j+(j-i+1)-\operatorname{fail}({j})-1}&(j-i+1) \bmod ((j-i+1)-\operatorname{fail}({j})) = 0\\ &\infty&\text{otherwise} \end{cases})&\text{otherwise}\\ \end{cases} \]

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;

int f[85][85],fail[85];
bool vis[85][85];
string str;
int n;

int dp(int l,int r){
    if(vis[l][r]) return f[l][r];
    vis[l][r]=1;
    if(l==r) return f[l][r]=1;
    f[l][r]=INT_MAX;
    for(int i=l;i<r;i++){
        f[l][r]=min(f[l][r],dp(l,i)+dp(i+1,r));
    }
    for(int i=1;i<=n;i++) fail[i]=0;
    for(int i=l+1,j;i<=r;i++){
        j=fail[i-1];
        while(j&&str[l+j]!=str[i]) j=fail[l+j-1];
        if(str[l+j]==str[i]) j++;
        fail[i]=j;
    }
    if((r-l+1)%(r-l+1-fail[r]) == 0){
        f[l][r]=min(f[l][r],dp(l,l+(r-l+1-fail[r])-1));
    }
    return f[l][r];
}

signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    while(cin>>str){
        if(str[0] == '*') break;
        memset(vis,0,sizeof(vis));
        n=str.size();str=" "+str;
        cout<<dp(1,n)<<'\n';
    }
    return 0;
}

标签:String,int,Factoring,UVA11022,vis,str,fail,operatorname,85
From: https://www.cnblogs.com/zheyuanxie/p/uva11022.html

相关文章

  • std::string::resize() 对缓冲区一些用处
    如果需要一个缓冲区来暂存字符串会先定义一个char*的数组来实现存完后又给string赋值,感觉有点麻烦,寻思有什么方法可以更优雅点比如如下代码1voidCVTString::StrToWS......
  • string的一些知识
    sizeof(string)为32因为本质上string属于类,类中的成员是char,类的大小就是类中成员变量(非静态)加上指向虚函数表的指针以及指向虚基类表的指针加起来的和。这里string类只有......
  • 23/1/119-LeetCode 08:String to Integer (atoi)
    思路主要是对于前面的零,可以不用再去特殊判断了嘛。直接当成普通的数字直接算就好,反正算完之后ans=0,nodifference;对于超出范围,这个一直都是我不太注意的地方,这里max=2^......
  • C++获取含有中文字符的string长度
    :前言造车轮的时候要用到中文字符串的长度辨别,发现char的识别不准,进行了一番研究。>开始研究在Windows下,中文字符在C++中的内存占用为2字节,此时采用字符串长度获取函......
  • string 接收 char 随机数abcd
    packagecom.fqs.demo;importjava.util.Random;publicclassCharAB{//输出26个小写字母和26个大写字母publicstaticvoidmain(String[]args){......
  • C++ 中标准库类型 string
    标准库类型string表示可变的字符序列,使用string类型必须首先包含string头文件。string本质上是一个类,是STL提供的char*的容器。定义初始化string对象初......
  • ABC 285 F - Substring of Sorted String
    好久都没写线段树的题解了……水一发题意:给定一个字符串,满足两种操作。第一种为修改串上某个地方的字母,第二种为查询一个区间,并判断当整个字符串按照升序排序后这一段区......
  • java:日期工具类,是否是闰年,获取当前日期的前后一天,月,年,获得日期的年月日时分秒,string与
    java:日期工具类,获取当前日期的前后一天,月,年,获得日期的年月日时分秒,string与date之间转换。这里写目录标题​​java:日期工具类,获取当前日期的前后一天,月,年,获得日期的年月日时......
  • StringBuilder类
    StringBuilder类/*StringBuilder是一个可变的字符串类,我们可以把它看作一个容器,可变是指它对象中的内容是可变的.String中的内容是不可变的.StringBuilder中的......
  • 使用StringBuilder拼接字符串
    使用StringBuilder拼接字符串/*StringBuilder比String来拼接字符串效率高!@#$需求:定义一个方法,把int数组中的数据按照指定的格式拼接成一个字符串返回,调用该方法,并在......