NOIP2020 T2字串匹配
阿新 • • 發佈:2020-12-15
一個字串,把它寫成\((AB)^iC\)的形式,並且要求\(f(A)\le f(C)\),其中\(f(S)\)表示字串\(S\)中出現了奇數次的字元。統計合法四元組\((A,B,C,i)\)(\(A,B,C\)不為空串,\(i>0\))的個數。
\(n\le 2^{20}\)
這就是NOIP題嗎,愛了愛了。
當場寫了個\(O(n\ln n+26n)\)的做法,用了雙雜湊常數有點大。而且我雜湊的時候是直接用迴圈串的性質判的,而不是對於每個迴圈節分別判,這導致我甚至沒有意識到可以break
。
聽說有人直接unsigned long long
雜湊過了?
可以發現性質:如果\((AB)^iC\)合法,那麼\((AB)^{i-2}C\)
列舉\(AB\)長度\(len\),分別考慮\(AB(AB)^{2i}C\)和\(ABAB(AB)^{2i}C\)的情況。以前者為例。
一種做法:還是雙雜湊,然後二分出最大的\(i\)。這一部分的時間是\(\sum \lg\frac{n}{i}=n\lg n-\sum \lg i\),估算一下\(\int \lg i=n\lg n-n\),然後發現時間複雜度是\(O(n)\)。
另一種做法:寫個exkmp,問\(LCP(s_{len+1\dots n},s_{1\dots n})\),就可以得到最大的\(i\)。
搞完這些這題基本做完了。
還需要查一下\(f(C)\)固定時合法的\(f(A)\)
using namespace std; #include <cstdio> #include <cstring> #include <algorithm> #define N (1<<20|5) #define ll long long int n; char s[N]; int ex[N]; void init(){ ex[1]=n; int p=0,mx=0; for (int i=2;i<n;++i){ ex[i]=0; if (i<=mx) ex[i]=min(mx-i+1,ex[i-p+1]); while (s[1+ex[i]]==s[i+ex[i]] && i+ex[i]<n) ++ex[i]; if (i+ex[i]-1>mx) mx=i+ex[i]-1,p=i; } ex[n+1]=0; } int buc[27],cnt; int f[N]; int g[27]; int main(){ freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); int T; scanf("%d",&T); while (T--){ scanf("%s",s+1); n=strlen(s+1); init(); cnt=0,memset(buc,0,sizeof buc); f[n+1]=0; for (int i=n;i>=1;--i){ cnt-=(buc[s[i]-'a']&1); cnt+=(++buc[s[i]-'a']&1); f[i]=cnt; } // for (int i=1;i<=n;++i) // printf("%d ",f[i]); // printf("\n"); memset(g,0,sizeof g); cnt=0,memset(buc,0,sizeof buc); ll ans=0; for (int i=1;i<n;++i){ ll tmp=ans; ans+=(ll)g[f[i+1]]*(ex[i+1]/(2*i)+1); if (i+i+1<=n && ex[i+1]>=i) ans+=(ll)g[f[i+i+1]]*(ex[i+i+1]/(2*i)+1); // printf("%lld\n",ans-tmp); cnt-=(buc[s[i]-'a']&1); cnt+=(++buc[s[i]-'a']&1); for (int j=cnt;j<=26;++j) g[j]++; } printf("%lld\n",ans); } return 0; }