Scau 8633 回文划分 mancher + dp

时间限制:1000MS  内存限制:1000K
提交次数:169 通过次数:63

题型: 编程题   语言: G++;GCC

 

Description

    我们说一个字符串是回文串,那么意味着这个串从两边读起来的字母都是一样的。例如racecar是回文串,
然而fastcar则不是。
    对一个串的划分意思是将一个串划分为若干个部分。例如,racecar可以划分为race 和car两部分。给出
一个串,要把这个串划分为若干个回文串,那么至少要把这个串划分为多少部分?
例如
'racecar'已经是回文串,划分为1 个部分即可(这个部分就是racecar)。
'fastcar' 需要被划分为七个部分 ('f', 'a', 's', 't', 'c', 'a', 'r')。根据回文串的定义,单个字母也是回文串。
'aaadbccb' 分成可以被分为三个回文串 ('aaa', 'd', 'bccb')。找不到更少的划分方法。




输入格式

输入的第一行是数字T,表示输入文件含有T个CASE。之后有T行,每行有一个长度不大于1000的字
符串,全部由小写字母组成,中间没有空格。



输出格式

对于每个CASE,输出一个数字,表示对该字符串的回文串最小划分。



 

输入样例

3
racecar
fastcar
aaadbccb



 

输出样例

1
7
3



 

提示




 

来源

 PKKJ @ 07 GIS 1 

 

dp[i]表示前i个字符最少能被划分为回文串的个数

转移就很简单了:dp[i] = min(dp[j - 1]) + 1; 其中 1 <= j <= i 并且 (s[j]~s[i])是回文串

直接算复杂度是O(n^3),显然不行,可以用mancher预处理出vis[i][j], 表示(s[i]~s[j])是否是回文串

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 1005;
char s[N], str[N << 1];
bool vis[N][N];
int dp[N], p[N << 1], len;
void get(int n)
{
    int mx = 0, id = 0;
    for(int i = 0; i < n; ++i)
    {
        p[i] = mx > i ? min(p[2 * id - i], mx - i) : 1;
        while(str[i + p[i]] == str[i - p[i]]) p[i]++;
        if(i + p[i] > mx) {
            mx = i + p[i];
            id = i;
        }
    }
}
void pre(int n)
{
    memset(vis, false, sizeof vis);
    for(int i = 2; i < n; ++i)
    {
        if(i & 1) {
            int st = i - 1;
            int ed = i + 1;
            int to = i - p[i] + 1;
            while(st >= to) { vis[st >> 1][ed >> 1] = true; st -= 2; ed += 2; }
        }else {
            int id = i >> 1;
            vis[id][id] = true;
            int st = i - 2;
            int ed = i + 2;
            int to = i - p[i] + 1;
            while(st >= to) { vis[st >> 1][ed >> 1] = true; st -= 2; ed += 2; }
        }
    }
}
void init()
{
    int n = 0;
    str[n++] = '$';
    str[n++] = '#';
    for(int i = 0; i < len; ++i) {
        str[n++] = s[i];
        str[n++] = '#';
    }
    str[n] = 0;
    get(n);
    pre(n);

}
void solve()
{
    dp[0] = 0;
    for(int i = 1; i <= len; ++i)
    {
        dp[i] = 0x3f3f3f3f;
        for(int j = 1; j <= i; ++j) if(vis[j][i])
            dp[i] = min(dp[i], dp[j - 1] + 1);
    }
    printf("%d\n", dp[len]);
}
int main()
{
    int _; scanf("%d", &_);
    while(_ --)
    {
        scanf("%s", s);
        len = strlen(s);
        init();
        solve();
    }
    return 0;
}
  

 

posted @ 2015-10-26 22:06  JL_Zhou  阅读(455)  评论(0编辑  收藏  举报