ccz181078

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: :: 管理 ::

Description

给定一个字符串,由大小写字母组成。长度为N,1<=N<3*10^5 问其有多少个子串,这些子串本身是个回文串,或者其中的字符经过重组合后 也可以为回文串.

将每个不同字母随机分配一个非零值,并保证这些值之间在异或意义下线性无关

一个子串可以重排为回文串当且仅当串中字母对应的值的异或和为 0(所有字母出现偶数次)或某个字母对应的值(这个字母出现奇数次,其余字母出现偶数次)

处理出前缀和并用一个hashmap(散列函数不要用取模否则可能tle)维护在当前位置以前每个前缀和值出现了几次,扫一遍记录答案

时间复杂度O(52n)

#include<cstdio>
#include<cstdlib>
typedef unsigned long long u64;
const int P=4194304;
u64 X[P];
int t[P];
int n;
char s[300010];
u64 v=0,ans=0,h[256],hs[64];
char cs[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
int find(u64 x){
    int w=x&4194303;
    while(t[w]){
        if(X[w]==x)return t[w];
        w+=123457;
        if(w>=P)w-=P;
    }
    return 0;
}
void ins(u64 x){
    int w=x&4194303;
    while(t[w]){
        if(X[w]==x){
            ++t[w];
            return;
        }
        w+=123457;
        if(w>=P)w-=P;
    }
    X[w]=x;t[w]=1;
}
int main(){
    srand(29399);
    for(int i=0;i<52;i++){
        h[cs[i]]=1ull<<i;
        for(int j=0;j<i;j++)if(rand()&1)h[cs[i]]^=1ull<<j;
        hs[i]=h[cs[i]];
    }
    scanf("%d%s",&n,s+1);
    ins(0);
    for(int i=1;i<=n;i++){
        v^=h[s[i]];
        ans+=find(v);
        for(int i=0;i<52;i++)ans+=find(v^hs[i]);
        ins(v);
    }
    printf("%llu\n",ans);
    return 0;
}

 

posted on 2016-06-09 13:28  nul  阅读(350)  评论(0编辑  收藏  举报