首页 > 代码库 > BZOJ 1090 SCOI 2003 字符串折叠 区间DP

BZOJ 1090 SCOI 2003 字符串折叠 区间DP

题目大意:给出一个字符串,在不改变这个字符串的内容的情况下可以将它进行折叠,具体见题里说的吧。问这个字符串最短可以折叠成多长。


思路:数据范围才100,怎么暴力怎么搞。首先是一个区间DP,设f[i][j]为字符串从i开始到j最短可以折叠成多短。要用到体中的折叠的方法,其实只需要暴力枚举这一段折叠成几段,然后用hash判定一下就行了。

当然不要忘了正常的区间DP。


CODE:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 110
#define BASE 23333
#define INF 0x3f3f3f3f
using namespace std;
 
char s[MAX];
unsigned long long hash[MAX],power[MAX];
int table[MAX];
int f[MAX][MAX];
 
void Pretreatment()
{
    for(int i = 1; i <= 9; ++i)  table[i] = 1;
    for(int i = 10; i <= 99; ++i)    table[i] = 2;
    table[100] = 3;
    power[0] = 1;
    for(int i = 1; i < MAX; ++i) power[i] = power[i - 1] * BASE;
}
 
inline unsigned long long GetHash(int l,int r)
{
    return hash[r] - hash[l - 1] * power[r - l + 1];
}
 
inline int Divide(int l,int r,int p)
{
    if((r - l + 1) % p) return INF;
    int len = (r - l + 1) / p;
    unsigned long long base = GetHash(l,l + len - 1);
    for(int i = l; i <= r; i += len)
        if(GetHash(i,i + len - 1) != base)
            return INF;
    return table[p] + 2 + f[l][l + len - 1];
}
 
int main()
{
    Pretreatment();
    scanf("%s",s + 1);
    int length = strlen(s + 1);
    for(int i = 1; i <= length; ++i)
        hash[i] = hash[i - 1] * BASE + s[i];
    for(int i = 1; i <= length; ++i)
        for(int j = i; j <= length; ++j)
            f[i][j] = j - i + 1;
    for(int j = 1; j <= length; ++j)
        for(int i = 1; i + j - 1 <= length; ++i) {
            for(int k = 1; k <= j; ++k)
                f[i][i + j - 1] = min(f[i][i + j - 1],Divide(i,i + j - 1,k));
            for(int k = 1; k < j; ++k)
                f[i][i + j - 1] = min(f[i][i + j - 1],f[i][i + k - 1] + f[i + k][i + j - 1]);
        }
    cout << f[1][length] << endl;
    return 0;
}


BZOJ 1090 SCOI 2003 字符串折叠 区间DP