b_lg_字符串折叠(折叠与不折叠取最优)
给一个字符串,求它的最短折叠。例如AAAAAAAAAABABABCCD的最短折叠为:9(A)3(AB)CCD。
输入格式
仅一行,即字符串S,长度保证不超过100。
输出格式
仅一行,即最短的折叠长度。
输入
NEERCYESYESYESNEERCYESYESYES
输出
14
说明/提示
一个最短的折叠为:2(NEERC3(YES))
方法一:dp
折叠不一定比折叠后的字符串更短,所以这里有两种选择:
- 折叠:需要枚举的子串是否有循环节,如果有,计算长度即可
- 不折叠,则 f[i][j]=min(f[i][j], f[i][k]+f[k+1][j])
#include<bits/stdc++.h>
using namespace std;
const int N=105;
int m[N], f[N][N];
string s;
bool chk(int l, int r, int sz) {
for (int i=l; i<=r-sz; i++) if (s[i]!=s[i+sz])
return false;
return true;
}
void init() {
for (int i=1; i<10; i++) m[i]=1;
for (int i=10; i<100; i++) m[i]=2;
m[100]=3;
}
int main() {
std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin>>s;
int n=s.size(); s=" "+s;
init();
memset(f, 0x3f3f3f3f, sizeof f);
for (int i=1; i<=n; i++) f[i][i]=1;
for (int len=2; len<=n; len++)
for (int i=1; i<=n-len+1; i++) {
int j=i+len-1;
for (int k=i; k<j; k++) {
int sz=k-i+1; //sz是循环节的大小
if (len%sz==0 && chk(i,j,sz)) {
f[i][j]=min(f[i][j], f[i][k]+2+m[len/sz]);
}
}
for (int k=i; k<j; k++) {
f[i][j]=min(f[i][j], f[i][k]+f[k+1][j]);
}
}
cout << f[1][n];
return 0;
}
复杂度分析
- Time:\(O(n^3)\),
- Space:\(O(n^2)\),