P4022 [CTSC2012]熟悉的文章 题解
对模式串建广义SAM,对于每个匹配串,首先求出 \(sl_i\) 表示以 \(i\) 为结尾的后缀的最长匹配长度。设 \(dp_i\) 表示 \(1\sim i\) 的最长匹配长度和,二分答案 \(mid\) 之后,有如下 dp 方程:
\[dp_i=\max(dp_{i-1},\max_{i-sl_i\leq j\leq i-mid}dp_j+i-j)
\]
发现这个 \(i-sl_i\) 和 \(i-mid\) 都单调不减,所以可以直接单调队列优化 dp,复杂度 \(O(|s|\log |s|)\)。
点击查看代码
const int N=5e5+13;
namespace ST{
const int logN=23;
#define log2 _log2
int log2[N],f[N][logN];
inline void _init(int n){for(int i=2;i<=n;++i)log2[i]=log2[i>>1]+1;}
inline void init(int n){
_init(n);
int k=log2[n]+1;
for(int j=1;j<=k;++j)
for(int i=1;i+(1<<j)-1<=n;++i) f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
inline int query(int l,int r){int k=log2[r-l+1];return min(f[l][k],f[r-(1<<k)+1][k]);}
#undef log2
}
std::vector<int> a[N<<1];
#define P(i) a[(i)+N]
int n,suf[N];
char s[N];
int nxt[N<<1],len[N<<1],ed[N<<1],ptot=1,lastpos=1,son[N<<1][2],zrzak[2];
inline int newpos(int nson[2],int nlen){return len[++ptot]=nlen,son[ptot][0]=nson[0],son[ptot][1]=nson[1],ptot;}
inline void insert(int c,int pos){
int p=lastpos,u=newpos(zrzak,len[p]+1);ed[u]=pos;
while(p&&!son[p][c]) son[p][c]=u,p=nxt[p];
lastpos=u;
if(!p) return nxt[u]=1,void();
int d=son[p][c];
if(len[d]==len[p]+1) nxt[u]=d;
else{
int v=newpos(son[d],len[p]+1);ed[v]=ed[d];
nxt[v]=nxt[d],nxt[d]=nxt[u]=v;
while(p&&son[p][c]==d) son[p][c]=v,p=nxt[p];
}
}
inline bool check(int k,int pos){return ST::query(k,pos-1)>=suf[pos];}
int main(){
read(n);read(s+1);
for(int i=1;i<=n;++i) insert(s[i]=='(',i);
for(int i=n;i;--i) suf[i]=suf[i+1]+(s[i]=='('?-1:1),ST::f[i][0]=suf[i];
for(int i=1;i<=n;++i) P(suf[i]).pb(i);
ST::init(n);ll ans=0;
for(int i=2;i<=ptot;++i){
int pos=ed[i]+1;int L=ed[i]-len[i]+1,R=ed[i]-len[nxt[i]];
int l=std::lower_bound(P(suf[pos]).begin(),P(suf[pos]).end(),L)-P(suf[pos]).begin(),r=std::upper_bound(P(suf[pos]).begin(),P(suf[pos]).end(),R)-P(suf[pos]).begin()-1;
int tmp=r;
if(l>r||!check(P(suf[pos])[r],pos)) continue;
while(l<r){
int mid=(l+r)>>1;
if(check(P(suf[pos])[mid],pos)) r=mid;
else l=mid+1;
}
ans+=tmp-l+1;
}
println(ans);
return 0;
}