【Atcoder】CODE FESTIVAL 2017 qual C D - Yet Another Palindrome Partitioning

【题意】给定只含小写字母的字符串,要求分割成若干段使段内字母重组顺序后能得到回文串,求最少分割段数。n<=2*10^5

【算法】DP

【题解】关键在于快速判断一个字符子串是否合法,容易发现合法仅当不存在或只存在一个奇数字符,其余字符均为偶数。

当涉及到奇偶性(%2)时,很自然能想到异或。

将小写字母a~z转化2^0~2^25,那么一个字符子串合法当且仅当其连续异或值是0或2^i(0<=i<=25)

令f[i]表示前i个合法的最少段数,sum[i]表示异或前缀和,则有:

f[i]=min(f[j])+1,sum[i]^sum[j]=0||2^i,也就是在前面所有合法的j中取最小的f[j]。

将合法条件移项,得到sum[i]^(0||2^i)=sum[j],那么对于当前的i,可以快速算出需要的sum[j]。

而sum值只有2*10^5个,可以用map存起来,然后就可以快速取用。

或者sum值本身不大,根据题目空间直接开数组也没问题。

复杂度O(26*n)。

#include<cstdio>
#include<cstring>
#include<cctype>
#include<cmath>
#include<algorithm>
#include<map>
#define ll long long
using namespace std;
int read(){
    char c;int s=0,t=1;
    while(!isdigit(c=getchar()))if(c=='-')t=-1;
    do{s=s*10+c-'0';}while(isdigit(c=getchar()));
    return s*t;
}
int min(int a,int b){return a<b?a:b;}
int max(int a,int b){return a<b?b:a;}
int abs(int x){return x>0?x:-x;}
void mins(int &a,int b){if(a>b)a=b;}
void maxs(int &a,int b){if(a<b)a=b;}
//void insert(int u,int v){tot++;e[tot].v=v;e[tot].from=first[u];first[u]=tot;}
/*------------------------------------------------------------*/
const int inf=0x3f3f3f3f,maxn=200010,maxN=70000010;
 
int n,p[maxN],f[maxn],sum;
char s[maxn];
 
int main(){
    scanf("%s",s+1);
    n=strlen(s+1);
    f[0]=0;
    memset(p,0x3f,sizeof(p));
    p[0]=0;
    for(int i=1;i<=n;i++){
        int x=1<<(s[i]-'a');
        sum^=x;
        f[i]=inf;
        f[i]=min(f[i],p[sum]+1);
        for(int j=0;j<26;j++)f[i]=min(f[i],p[sum^(1<<j)]+1);
        p[sum]=min(p[sum],f[i]);
    }
    printf("%d",f[n]);
    return 0;
}
View Code

 

posted @ 2017-10-24 21:14  ONION_CYC  阅读(173)  评论(0编辑  收藏  举报