首页 > 技术文章 > b_lg_字符串折叠(折叠与不折叠取最优)

wdt1 2020-09-15 15:07 原文

给一个字符串,求它的最短折叠。例如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)\)

推荐阅读