字符串算法学习笔记
目录
字符串算法学习笔记
天坑,大概暑假就能填完了。
符号使用说民
一般用\(S,T\)表示一个字符串
字符串一般从1开始
用\(S[l\ldots r]\)表示一个子串
用\(S\in T\)表示\(S\)是\(T\)的子串
题目后的\(\text{easy/normal/hard/lunatic}\)表示题目的难度(大概对应NOIp d1t1+/NOIp d1d2+/弱省省选/强省省选)
kmp学习笔记
exkmp
咕了(
AC自动机总结
假设你们都懂AC自动机是啥了
fail树
\(\text{fail}\)树就是连接所有\(fail_u\)和\(u\)的树。
考虑到\(fail_u\)所代表的串\(\in u\)所代表的串
所以若u所代表的串\(\in S\),则在fail树上u到根的所有节点代表的串也\(\in S\)
我们来看几个例题学习一下
P3966 [TJOI2013]单词 (normal)
将fail树建出来。
然后将每一个串所走过的位置+1
然后每一个串的出现次数就是结束位置的fail树中的子树的权值之和。
代码如下
#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef vector<int > vi;
typedef pair<int ,int > pii;
const int INF=0x3f3f3f3f, maxn=1000007;
const ll MOD=1004535809;
const ll LINF=0x3f3f3f3f3f3f3f3fLL;
const ll P=19260817;
int n;
char s[maxn];
int tr[maxn][26];
int fail[maxn];
int num[maxn];
int tot=1;
int sum[maxn];
void ins(char *s,int id){
int len=strlen(s);
int u=1;
for(int i=0;i<len;i++){
int c=s[i]-'a';
if(!tr[u][c])tr[u][c]=++tot;
u=tr[u][c];
sum[u]++;
}
num[id]=u;
}
struct edge{
int to,nxt;
}e[maxn<<1];
int head[maxn],tot_e;
void addedge(int u,int v){
e[++tot_e]=(edge){v,head[u]};
head[u]=tot_e;
}
queue<int > q;
void build(){
for(int i=0;i<26;i++)tr[0][i]=1;
q.push(1);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=0;i<26;i++){
if(tr[u][i])fail[tr[u][i]]=tr[fail[u]][i],q.push(tr[u][i]);
else tr[u][i]=tr[fail[u]][i];
}
}
for(int i=2;i<=tot;i++){
addedge(fail[i],i);
}
}
void dfs(int u,int f){
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==f)continue;
dfs(v,u);
sum[u]+=sum[v];
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%s",s);
ins(s,i);
}
build();
dfs(1,0);
for(int i=1;i<=n;i++)printf("%d\n",sum[num[i]]);
return 0;
}
P2414 [NOI2011]阿狸的打字机(hard)
将所有查询离线并建一个ac自动机。
这个串的ac自动机事实上很好建。
遇到P就标记一下,遇到B就跳到他父亲。
我们考虑一组查询(x,y)表示x在y中出现了多少次
相当与在trie中把y到根的所有节点+1,然后查询在fail树中的子树之和。
所以我们dfs一遍trie树,如果我们遍历到一个串y,我们就查询所有它所有串。
查询子树和我们就对在fail树中dfs序建一棵BIT就行了.
写起来细节有点多
#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define y0 pmt
#define y1 pmtpmt
#define x0 pmtQAQ
#define x1 pmtQwQ
// #define getchar nc
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef vector<int > vi;
typedef pair<int ,int > pii;
const int INF=0x3f3f3f3f, maxn=200007;
const ll MOD=1004535809;
const ll LINF=0x3f3f3f3f3f3f3f3fLL;
const ll P=19260817;
char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline ll read(){
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=((x<<3)+(x<<1)+ch-'0')%MOD,ch=getchar();
return x*f;
}
void write(int x){
if(!x)putchar('0');if(x<0)x=-x,putchar('-');
static int sta[20];register int tot=0;
while(x)sta[tot++]=x%10,x/=10;
while(tot)putchar(sta[--tot]+48);
}
int tr[maxn][26];
int fail[maxn];
int f[maxn];
int cnt=1,id_n=0;
int num[maxn];
int tag[maxn];
int vis[maxn][26];
void ins(char *s){
int u=1,len=strlen(s);
for(int i=0;i<len;i++){
if(s[i]=='P'){
num[++id_n]=u;
tag[u]=1;
}else if(s[i]=='B'){
u=f[u];
}else {
int c=s[i]-'a';
if(!tr[u][c]){tr[u][c]=++cnt;f[tr[u][c]]=u;}
u=tr[u][c];
}
}
}
struct edge{
int to,nxt;
}e[maxn<<1];
int head[maxn],tot_e;
void addedge(int u,int v){
e[++tot_e]=(edge){v,head[u]};
head[u]=tot_e;
}
queue<int > q;
void build(){
for(int i=0;i<26;i++)tr[0][i]=1;
q.push(1);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=0;i<26;i++){
if(tr[u][i])fail[tr[u][i]]=tr[fail[u]][i],q.push(tr[u][i]);
else tr[u][i]=tr[fail[u]][i];
}
}
for(int i=2;i<=cnt;i++){
addedge(fail[i],i);
}
}
int dfn_cnt;
int dfn[maxn];
int sz[maxn];
void dfs1(int u,int f){
dfn[u]=++dfn_cnt;
sz[u]=1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==f)continue;
dfs1(v,u);
sz[u]+=sz[v];
}
}
struct BIT{
int t[maxn];
#define N 200000
void update(int x,int val){x++;for(int i=x;i<=N;i+=(i&-i))t[i]+=val;}
int query(int x){x++;int rt=0;for(int i=x;i;i-=(i&(-i)))rt+=t[i];return rt;}
}T;
vector<pii > query[maxn];
int ans[maxn];
void dfs2(int u){
// cout<<u<<endl;
T.update(dfn[u],1);
if(tag[u]){
// cout<<u<<' '<<query[u].size()<<endl;
for(int i=0;i<query[u].size();i++){
int v=query[u][i].fi;
ans[query[u][i].se]=T.query(dfn[v]+sz[v]-1)-T.query(dfn[v]-1);
}
}
// cout<<u<<' '<<tag[u]<<endl;
for(int i=0;i<26;i++){
if(tr[u][i]&&vis[u][i])dfs2(tr[u][i]);
}
T.update(dfn[u],-1);
}
char s[maxn];
int n;
int main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
scanf("%s",s);
ins(s);
build();
dfs1(1,0);
scanf("%d",&n);
for(int i=1,x,y;i<=n;i++){
scanf("%d%d",&x,&y);
query[num[y]].pb(mp(num[x],i));
}
dfs2(1);
for(int i=1;i<=n;i++)printf("%d\n",ans[i]);
return 0;
}
CF1207G Indie Album
和上一题差不多吧……
后面补SAM做法……。
#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define y0 pmt
#define y1 pmtpmt
#define x0 pmtQAQ
#define x1 pmtQwQ
// #define getchar nc
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef vector<int > vi;
typedef pair<int ,int > pii;
const int INF=0x3f3f3f3f, maxn=800007;
const ll MOD=1004535809;
const ll LINF=0x3f3f3f3f3f3f3f3fLL;
const ll P=19260817;
char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline ll read(){
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=((x<<3)+(x<<1)+ch-'0')%MOD,ch=getchar();
return x*f;
}
void write(int x){
if(!x)putchar('0');if(x<0)x=-x,putchar('-');
static int sta[20];register int tot=0;
while(x)sta[tot++]=x%10,x/=10;
while(tot)putchar(sta[--tot]+48);
}
int n,m;
char s[maxn];
vector<pair<int ,int > > t[maxn];
void ins1(){
for(int i=1;i<=n;i++){
int op;
char c[2];
scanf("%d",&op);
if(op==1){
scanf("%s",c);
t[1].pb(mp(c[0]-'a',i+1));
}else {
int j;
scanf("%d%s",&j,c);
t[j+1].pb(mp(c[0]-'a',i+1));
}
}
}
int tr[maxn][26];
int fail[maxn];
int tot_cnt=1;
int num[maxn];
vector<pii > vec[maxn];
void ins(char *s,int id){
int u=1,len=strlen(s);
for(int i=0;i<len;i++){
int c=s[i]-'a';
if(!tr[u][c])tr[u][c]=++tot_cnt;
u=tr[u][c];
}
num[id]=u;
}
struct edge{
int to,nxt;
}e[maxn<<1];
int head[maxn],tot_e;
void addedge(int u,int v){
e[++tot_e]=(edge){v,head[u]};
head[u]=tot_e;
}
queue<int > q;
void build(){
for(int i=0;i<26;i++)tr[0][i]=1;
q.push(1);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=0;i<26;i++){
if(tr[u][i])fail[tr[u][i]]=tr[fail[u]][i],q.push(tr[u][i]);
else tr[u][i]=tr[fail[u]][i];
}
}
for(int i=2;i<=tot_cnt;i++){
addedge(fail[i],i);
}
}
int dfn_cnt;
int dfn[maxn];
int sz[maxn];
void dfs1(int u,int f){
dfn[u]=++dfn_cnt;
sz[u]=1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==f)continue;
dfs1(v,u);
sz[u]+=sz[v];
}
}
struct BIT{
int t[maxn];
#define N 500000
void update(int x,int val){x++;for(int i=x;i<=N;i+=(i&-i))t[i]+=val;}
int query(int x){x++;int rt=0;for(int i=x;i;i-=(i&(-i)))rt+=t[i];return rt;}
}T;
int ans[maxn];
void dfs2(int u1,int u2){
T.update(dfn[u2],1);
for(int i=0;i<vec[u1].size();i++){
int v=vec[u1][i].fi;
ans[vec[u1][i].se]=T.query(dfn[v]+sz[v]-1)-T.query(dfn[v]-1);
}
for(int i=0;i<t[u1].size();i++){
int c=t[u1][i].fi;
int v=t[u1][i].se;
dfs2(v,tr[u2][c]);
}
T.update(dfn[u2],-1);
}
int main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
scanf("%d",&n);
ins1();
scanf("%d",&m);
for(int i=1;i<=m;i++){
int x;
scanf("%d%s",&x,s);
ins(s,i);
vec[x+1].pb(mp(num[i],i));
}
build();
dfs1(1,0);
dfs2(1,1);
for(int i=1;i<=m;i++){
printf("%d\n",ans[i]);
}
return 0;
}
AC自动机上DP
P4052 [JSOI2007]文本生成器
设\(dp[i][j]\)表示构造一个长为i,匹配到j节点的串的不合法(即没有合法子串)方案数。
转移显然
最后拿\(26^m\)减一下就好了。
#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define y0 pmt
#define y1 pmtpmt
#define x0 pmtQAQ
#define x1 pmtQwQ
// #define getchar nc
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef vector<int > vi;
typedef pair<int ,int > pii;
const int INF=0x3f3f3f3f, maxn=20007;
const ll MOD=10007;
const ll LINF=0x3f3f3f3f3f3f3f3fLL;
const ll P=19260817;
char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline ll read(){
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=((x<<3)+(x<<1)+ch-'0')%MOD,ch=getchar();
return x*f;
}
void write(int x){
if(!x)putchar('0');if(x<0)x=-x,putchar('-');
static int sta[20];register int tot=0;
while(x)sta[tot++]=x%10,x/=10;
while(tot)putchar(sta[--tot]+48);
}
int tr[maxn][26];
int fail[maxn];
int dng[maxn];
int tot=1;
void ins(char *s){
int u=1,len=strlen(s);
for(int i=0;i<len;i++){
int c=s[i]-'A';
if(!tr[u][c])tr[u][c]=++tot;
u=tr[u][c];
}
dng[u]=1;
}
struct edge{
int to,nxt;
}e[maxn<<1];
int head[maxn],tot_e;
void addedge(int u,int v){
e[++tot_e]=(edge){v,head[u]};
head[u]=tot_e;
}
queue<int > q;
void build(){
for(int i=0;i<26;i++)tr[0][i]=1;
q.push(1);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=0;i<26;i++){
if(tr[u][i])fail[tr[u][i]]=tr[fail[u]][i],q.push(tr[u][i]);
else tr[u][i]=tr[fail[u]][i];
}
}
for(int i=2;i<=tot;i++){
// addedge(u,fail[u]);
addedge(fail[i],i);
}
}
void dfs(int u,int flag){
dng[u]|=flag;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
dfs(v,dng[u]);
}
}
ll qpow(ll a,ll b){
ll rt=1;
while(b){
if(b&1)rt=rt*a%MOD;
a=a*a%MOD;
b>>=1;
}
return rt;
}
int n,m;
char s[maxn];
int dp[107][maxn];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%s",s);
ins(s);
}
build();
dfs(1,0);
dp[0][1]=1;
for(int i=1;i<=m;i++){
for(int j=1;j<=tot;j++){
for(int c=0;c<26;c++){
if(!dng[tr[j][c]])dp[i][tr[j][c]]=(dp[i][tr[j][c]]+dp[i-1][j])%MOD;
}
}
}
ll ans=qpow(26,m);
for(int i=1;i<=tot;i++){
ans=(ans-dp[m][i]+MOD)%MOD;
}
printf("%lld",ans);
return 0;
}