切割回文

题目描述

阿福最近对回文串产生了非常浓厚的兴趣。

如果一个字符串从左往右看和从右往左看完全相同的话,那么就认为这个串是一个回文串。例如,abcaacba 是一个回文串,abcaaba 则不是一个回文串。

阿福现在强迫症发作,看到什么字符串都想要把它变成回文的。阿福可以通过切割字符串,使得切割完之后得到的子串都是回文的。

现在阿福想知道他最少切割多少次就可以达到目的。例如,对于字符串 abaacca,最少切割一次,就可以得到 abaacca 这两个回文子串。

输入

输入的第一行是一个整数 TTT (T≤20)(T \le 20)(T20) ,表示一共有 TTT 组数据。 接下来的 TTT 行,每一行都包含了一个长度不超过的 100010001000 的字符串,且字符串只包含了小写字母。

输出

对于每组数据,输出一行。该行包含一个整数,表示阿福最少切割的次数,使得切割完得到的子串都是回文的。

输入输出样例

样例输入 #1

3
abaacca
abcd
abcba

样例输出 #1

1
3
0

提示

对于第一组样例,阿福最少切割 111 次,将原串切割为 abaacca 两个回文子串。

对于第二组样例,阿福最少切割 333 次,将原串切割为 abcd 这四个回文子串。

对于第三组样例,阿福不需要切割,原串本身就是一个回文串。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int f[N],a[N],n,res,m,t;
bool vis[1010][1010];
string s;
bool check(int st,int ed)//用于判断是不是回文串
{
    while(st<=ed){
        if(s[st]==s[ed]) st++,ed--;
        else return false;
    }
    return true;
}
int main()
{
    cin>>t;
    while(t--)
    {
        memset(f,1,sizeof f);//因为要求最小值,所以先初始化为最大
        memset(vis,false,sizeof vis);
        cin>>s;
        res=s.size();
        for(int i=0;i<res;i++)
            for(int j=i+1;j<res;j++) if(check(i,j)) vis[i][j]=true;//判断每个片段是否是回文串
        f[1]=0;//第一个字符不需要切割也是,所以为0
        for(int i=2;i<=res;i++)
        {
            if(vis[0][i-1]) f[i]=0;//如果在这之前的是回文串那么从这个点开始就不需要切割
            else f[i]=f[i-1]+1;//否则+1,继续切割
            for(int j=i;j>=1;j--) if(vis[j-1][i-1])  f[i]=min(f[i],f[j-1]+1);//然后从这之前再判断,有可能前面存在f=0,更新状态
        }
        cout<<f[res]<<endl;
    }
    return 0;
}

 

posted @ 2023-06-10 09:58  o-Sakurajimamai-o  阅读(52)  评论(0)    收藏  举报
-- --