枫林晚 的博客

枫林晚 的博客

序列自动机——所有公共子序列问题

posted on 2018-06-11 21:30:21 | under 未分类 |

序列自动机:

是一个处理子序列的自动机。就这样。

建造:(By猫老师:immoralCO猫)

s[]
next[][26]
memset(next[n], -1, 26<<2);
for(int i = n; i; --i) {
    memcpy(next[i - 1], next[i], 26 << 2);
    next[i - 1][s[i] - 'a'] = i;
}

nxt[][]数组就是第几个位置,序号为几的出边连接到第几个位置(位置是对应字符串的位置,其实并没用)

大概原理就是每当要循环到字符串中的一个位置,就把这个位置的连通性赋值给上一个节点编号,(可以理解,n个字符,其实是n条边,最多有n+1个节点在两边)

然后处理新来的字符i对于i-1号位置连通性的影响,那么,

编号从0~n,其中0号点就是根,dfs从0开始。

(不会的话,手动模拟就好了)

发现,当子序列中有重复元素的时候,nxt[i-1][s[i]-'a']=i一句可以将这种情况覆盖掉。

由于这些0~n号节点可以重复到达,当然最终到了n号点就是边界了。

所以dfs没有问题。而且大大节省了空间。

这样,我们可以只用有限的O(长度*|S|)的空间,来建造这棵树。

发现,这棵树好像trie啊~!!!!

其实差不多,一个子序列,一个子串。

操作也就和trie差不多了。

基本操作:

1.可以统计一个串本质不同的子序列的个数

序列自动机上可以是一棵树,树上每一个节点到根的路径上的边所代表的字符串就是所有的本质不同的子序列。

dfs树上扫一遍就好了。

2.可以查找一个子序列是否在这个字符串中出现过。

显然,dfs就可以。

3.也可以两个序列自动机一起dfs,找到所有公共子序列。

就比如说这个题:

(真·序列自动机板子题)

[FJOI2016]所有公共子序列问题

题目大意:给定两个字符串,求这两个串的所有公共子序列。

当输入的参量k=1的时候,按照字典序输出这些子序列,并输出个数。

当输入的参量k=0的时候,输出个数就可以。

注意,空字符串也是一个公共子序列。

分析:

裸裸裸裸的序列自动机。

开两个自动机,直接同时跑dfs就可以。

对于k=1,就要先走a,再走z,条件是两个都可以走,一遍用一个栈一样的字符串记录字符串。进入循环就输出即可。并且记录总数。

对于k=0,同理。

诶,怎么我的long long出了负数呢??

因为要高精。

诶,怎么我的高精MLE了呢????

因为要压位高精。

https://www.cnblogs.com/Miracevin/p/9031691.html

但是这个版本太弱了,很久以前写的。

所以,用结构体实现就比较方便了。结构体内置函数。

支持:高精加低精(因为要赋初值1(其实直接赋值也可以)),高精加高精,压位高精的输出。

没了。

看代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9;
const int N=3020;
const int L=58;
const int K=25;
int nxt1[N][L],nxt2[N][L];
char sta[N];
int top=-1;
ll ans;
int la,lb,k;
char a[N],b[N];
struct Big{//压位结构体 
    int cur;
    ll *s;
    void init(){
        s=new long long[20];
        for(int i=0;i<20;i++) s[i]=0;
        cur=0;
    }
    void put(){
        printf("%lld",s[cur]);
        for(int i=cur-1;i>=0;i--) printf("%09lld",s[i]);
    }
    void add(ll k){
        s[0]+=k;
        int i=0;
        while(s[i]>=mod) s[i+1]+=s[i]/mod,s[i++]%=mod;
        while(s[cur+1]) cur++;
    }
    void Add(const Big& o){
        ll i,r=max(cur,o.cur);
        for(int i=0;i<=r;i++){
            s[i]+=o.s[i];
            if(s[i]>=mod) s[i+1]+=s[i]/mod,s[i]%=mod;
        }
        cur=min(r+3,19ll);while(cur&&s[cur]==0) cur--;
    }
}dp[N][N];
bool vis[N][N];
void build1(){//建造序列自动机 
    memset(nxt1[la],-1,sizeof nxt1[la]);
    for(int i=la;i;i--){
        memcpy(nxt1[i-1],nxt1[i],sizeof nxt1[i]);
        nxt1[i-1][a[i]-'A']=i;
    }
}
void build2(){
    memset(nxt2[lb],-1,sizeof nxt2[lb]);
    for(int i=lb;i;i--){
        memcpy(nxt2[i-1],nxt2[i],sizeof nxt2[i]);
        nxt2[i-1][b[i]-'A']=i;
    }
}
void dfs2(int x,int y){//dfs 
    if(vis[x][y]) return;
    vis[x][y]=1;
    dp[x][y].init();
    dp[x][y].add(1);
    for(int i=0;i<=57;i++){
        if(nxt1[x][i]!=-1&&nxt2[y][i]!=-1) {
        dfs2(nxt1[x][i],nxt2[y][i]);
        dp[x][y].Add(dp[nxt1[x][i]][nxt2[y][i]]);
        }
    }
}
ll dfs1(int x,int y){
    printf("%s\n",sta);
    ll cnt=1;
    for(int i=0;i<=57;i++){
        if(nxt1[x][i]!=-1&&nxt2[y][i]!=-1) {
        sta[++top]=i+'A';
        cnt+=dfs1(nxt1[x][i],nxt2[y][i]);
        sta[top--]=' ';
        }
    }
    return cnt;
}
int main()
{
    scanf("%d%d",&la,&lb);
    scanf("%s",a+1);scanf("%s",b+1);    
    scanf("%d",&k);
    build1();build2();
    if(k==1) {    
    dfs1(0,0);
    }
    dfs2(0,0);
    dp[0][0].put();
    return 0;
}