字符串算法学习笔记

字符串算法学习笔记

天坑,大概暑假就能填完了。

符号使用说民

一般用\(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\)所代表的串

扫描二维码关注公众号,回复: 8862293 查看本文章

所以若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;
}

杂题

后缀数据结构学习

猜你喜欢

转载自www.cnblogs.com/pmt2018/p/12234502.html