序列自動機總結與例題


構造

\(a\)是字符集,\(|s|=n\)\(nxt[i][j]\)表示\(i\)以后的第一個字符\(j\)的位置,\(0\)為根節點,整個圖是一個\(DAG\)

for(LL i=n;i>=1;--i){
    for(LL j=1;j<=a;++j) nxt[i-1][j]=nxt[i][j];
    nxt[i-1][s[i]]=i;
}

擴展構建

當字符集較大時,可套用可持久化,在葉子節點放一個\(id\),表示出邊

相關例題:
字符串\(K\)小子序列,可持久化序列自動機,維護節點大小

一步一步(從首到尾)走,有序確定code

經典例題

判斷是否是原字符串的子序列

構造出了\(nxt\)后,從根跑一遍就好了

求子序列個數

從根跑,記憶化搜索,\(f[x]\)為點\(x\)為首的子序列個數,\(f[y]=(\sum\limits_{x\in y'son}f[x])+1\)

求兩串的公共子序列個數

兩串都構造一下,之間跑就好了

LL Dfs(LL x,LL y){
    if(f[x][y]) return f[x][y];
    for(LL i=1;i<=a;++i)
        if(nxt1[x][i]&&nxt2[y][i])
            f[x][y]+=Dfs(nxt1[x][i],nxt2[y][i]);
    return ++f[x][y];
}

求字符串的回文子序列個數

原串與反串都建一遍

\[\begin{aligned}\longrightarrow 1~~2~~3~~4~~5~~6~~7~~8~~9~~10&\\ 10~~9~~8~~7~~6~~5~~4~~3~~2~~1&\longleftarrow\\ \end{aligned}\]

就相當於從左右端點這樣跑

求的時候顯然\(x+y≤n+1\)這個序列才是合法的

\(x+y=n+1\)時就是會合了一樣,在之后的遍歷過程會\(++f[x][y]\),所以暫時不統計

但是其他情況我們都是匹配的兩個字符,也就是只會統計\(abba\),而統計不了\(aba\),所以在過程中\(++f[x][y]\)

LL Dfs(LL x,LL y){
	if(f[x][y]) return f[x][y];
	for(LL i=1;i<=a;++i)
		if(nxt1[x][i]&&nxt2[y][i]){
			if(nxt1[x][i]+nxt2[y][i]>n+1) continue;
			if(nxt1[x][i]+nxt2[y][i]<n+1) f[x][y]++;
			f[x][y]=(f[x][y]+Dfs(nxt1[x][i],nxt2[y][i]))%mod;
		}
	return ++f[x][y];
}

求一個\(A,B\)的最長公共子序列\(S\),使得\(C\)\(S\)的子序列

還是同樣的\(Dfs(x,y,z)\),表示一匹配到\(C\)\(z\)

改變一下\(C\)的構建方法

for(LL i=1;i<=a;++i) nxt[n][i]=n;
for(LL i=0;i<n;++i){
	for(LL j=1;j<=a;++j) nxt[i][j]=i;
	nxt[i][c[i+1]]=i+1;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM