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 ",ans); return 0; }