1. 程式人生 > 其它 >P4022 [CTSC2012]熟悉的文章 題解

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;
}