【洛谷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;
}
待到再迷茫时回头望,所有脚印会发出光芒