1. 程式人生 > 實用技巧 >【洛谷4965】薇爾莉特的打字機(假裝有棵Trie樹)

【洛谷4965】薇爾莉特的打字機(假裝有棵Trie樹)

點此看題面

  • 一開始有一個長度為\(n\)的字串,有\(m\)次操作:
    • 加入一個給定字母。
    • 刪除最後一個字母(如果串為空則忽略)。
  • 已知每次操作有可能成功有可能失敗,問最後可能得到多少種串。
  • \(n,m\le5\times10^6\)

\(Trie\)

考慮暴力的做法,我們直接建出一棵\(Trie\)樹。

如果刪去一個字元,就是跳回父節點,發現除初始鏈外所有節點都是原本就能得到的,因此這一操作就相當於初始鏈上有一個新的節點可以擴充套件兒子了。

每次加入一個新的字元,就給所有沒這個兒子的節點(初始鏈上尚未更新到的節點除外)加上這個兒子,並更新答案。

假裝有棵\(Trie\)

實際上我們並不需要知道到底有多少\(Trie\)

樹,只要知道對於每種字元,有多少個節點沒這個兒子。

\(f_i\)表示沒有第\(i\)個兒子的節點數。

加入一個字元\(x\),會新增\(f_x\)個節點,故除\(f_x\)以外的所有\(f_i\)加上\(f_x\)

刪去一個字元,初始鏈的上一個節點就可以擴充套件兒子,因此給除了已有兒子以外的\(25\)\(f_i\)\(1\)

程式碼

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 5000000
#define X 19260817
#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
using namespace std;
int n,m,f[30];char s[N+5],ss[N+5];
int main()
{
	RI i,j,t,ans=1;for(scanf("%d%d%s%s",&n,&m,ss+1,s+1),i=1;i<=26;++i) f[i]=1;for(i=1;i<=m;++i)
	{
		if(s[i]=='u') {if(n) for(--n,++ans,j=1;j<=26;++j) f[j]+=(ss[n+1]&31)!=j;continue;}//初始鏈上跳
		for(t=f[s[i]&31],Inc(ans,t),j=1;j<=26;++j) (s[i]&31)^j&&Inc(f[j],t);//新增節點數即為沒有該兒子的節點數
	}return printf("%d\n",ans),0;
}