题解 P1117 【[NOI2016]优秀的拆分 】
bztMinamoto · · 题解
听说大佬们都是用SA做的
然而SA的时间复杂度的确很优秀,缺点就是看不太懂……
然后发现一位大佬用哈希华丽的过了此题,而且讲的特别清楚->这里
我们只要考虑以每一个点结尾的
那么考虑如何求出
我们考虑一下,枚举串
然后考虑,只要求出相邻两个关键点往前的
说了这么多,到底怎么求
时间复杂度是枚举
ps:话说我也不明白调和级数是个什么玩意儿,只要知道枚举的复杂度是
pps:管那么多干嘛能A不就行了么……话说这题明明纯哈希暴力就有95……某大佬讲课的时候还以这题为例嘲笑NOI近几年的出题水平(逃)
//minamoto
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
#define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<21],*p1=buf,*p2=buf;
inline int read(){
#define num ch-'0'
char ch;bool flag=0;int res;
while(!isdigit(ch=getc()))
(ch=='-')&&(flag=true);
for(res=num;isdigit(ch=getc());res=res*10+num);
(flag)&&(res=-res);
#undef num
return res;
}
char sr[1<<21],z[20];int C=-1,Z;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
inline void print(ll x){
if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x;
while(z[++Z]=x%10+48,x/=10);
while(sr[++C]=z[Z],--Z);sr[++C]='\n';
}
const int N=30005,mod=3e7+7;
char s[N];int n;
ll hash[N],mo[N],u[N],v[N],ans;
inline ll gethash(int l,int r){
ll now=hash[l]-hash[r]*mo[r-l];
now%=mod,now+=mod,now%=mod;
return now;
}
int main(){
int T=read();mo[0]=1;for(int i=1;i<=30000;++i) mo[i]=mo[i-1]*31%mod;
while(T--){
n=0;char ch;
while((ch=getc())!='\n') s[++n]=ch;
memset(u,0,sizeof(u)),memset(v,0,sizeof(v));
hash[n+1]=0;
for(int i=n;i;--i) (hash[i]=hash[i+1]*31+s[i]-'a'+1)%=mod;
for(int L=1;L*2<=n;++L){
for(int i=L<<1;i<=n;i+=L){
if(s[i]!=s[i-L]) continue;
int l=1,r=L,last=i-L,pos=0;
//二分查找lcp和lcs
while(l<=r){
int mid=l+r>>1;
if(gethash(last-mid+1,last+1)==gethash(i-mid+1,i+1)) pos=mid,l=mid+1;
else r=mid-1;
}
int head=i-pos+1;
l=1,r=L,pos=0;
while(l<=r){
int mid=l+r>>1;
if(gethash(last,last+mid)==gethash(i,i+mid)) pos=mid,l=mid+1;
else r=mid-1;
}
int tail=i+pos-1;
head=max(head+L-1,i);//防止越过两块
tail=min(tail,i+L-1);//防止跑到后面的块
if(head<=tail){
++u[head-2*L+1],--u[tail+1-2*L+1];
++v[head],--v[tail+1];
//为了差分
//因为head-2*L+1到tail-2*L+1开头的AA串增加的
//以他们的答案都可以++
//然后以head到tail结尾的AA串也++
}
}
}
ans=0;
for(int i=1;i<=n;++i) u[i]+=u[i-1],v[i]+=v[i-1];
for(int i=1;i<n;++i) ans+=v[i]*u[i+1];
print(ans);
}
Ot();
return 0;
}