bzoj1090 [SCOI2003]字符串折叠

1090: [SCOI2003]字符串折叠

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 1718  Solved: 1146
[Submit][Status][Discuss]

Description

折叠的定义如下: 1. 一个字符串可以看成它自身的折叠。记作S  S 2. X(S)是X(X>1)个S连接在一起的串的折叠。记作X(S)  SSSS…S(X个S)。 3. 如果A  A’, BB’,则AB  A’B’ 例如,因为3(A) = AAA, 2(B) = BB,所以3(A)C2(B)  AAACBB,而2(3(A)C)2(B)AAACAAACBB 给一个字符串,求它的最短折叠。例如AAAAAAAAAABABABCCD的最短折叠为:9(A)3(AB)CCD。

Input

仅一行,即字符串S,长度保证不超过100。

Output

仅一行,即最短的折叠长度。

Sample Input

NEERCYESYESYESNEERCYESYESYES

Sample Output

14

HINT

一个最短的折叠为:2(NEERC3(YES))

分析:一开始觉得和bzoj1068差不多.用那道题的状态去推状态转移方程发现不是那么容易推.因为这里括号必须要是匹配的,而那一题并不要求R,M匹配.

   实际上这道题还要简单一些,区间dp的基本状态表示方法:f[l][r]就能够转移.

   两种方式:1.f[l][r] = min{f[i][k] + f[k+1][r]}.  2.f[l][r] = min{f[l][k] + 2 + (r - l + 1) / (k - l + 1)的结果的位数}.这两个转移分别是将子区间合并和将字符串折叠.

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

char s[110];
int n,f[110][110];

int cal(int x)
{
    if (x >= 1 && x <= 9)
        return 1;
    if (x >= 10 && x <= 99)
        return 2;
    if (x >= 100)
        return 3;
}

bool check(int l,int mid,int r)
{
    int x = l;
    for (int i = mid + 1; i <= r; i++)
    {
        if (s[x] != s[i])
            return false;
        x++;
        if (x == mid + 1)
            x = l;
    }
    return true;
}

int main()
{
    scanf("%s",s + 1);
    n = strlen(s + 1);
    for (int l = 1; l <= n; l++)
    {
        for (int i = 1; i + l - 1 <= n; i++)
        {
            int j = i + l - 1;
            f[i][j] = j - i + 1;
            for (int k = i; k < j; k++)
                f[i][j] = min(f[i][j],f[i][k] + f[k + 1][j]);
            for (int k = i; k < j; k++)
            {
                int len = k - i + 1;
                if(l % len != 0)
                    continue;
                if (check(i,k,j))
                    f[i][j] = min(f[i][j],f[i][k] + 2 + cal(l / len));
            }
        }
    }
    printf("%d
",f[1][n]);

    return 0;
}
原文地址:https://www.cnblogs.com/zbtrs/p/8455192.html