kuangbin_ac automata

preface

k u a n g b i n kuangbin kuangbin's a c ac ac automata is still relatively basic, and most of the questions are based on the state machine model d p dp dp, two of which need to use matrix optimization to record the number of paths, and then about finding the shortest path on the tire diagram, and some need state pressure, etc. there are few questions for string matching. There is no more nonsense. Let's roughly divide them into categories first.

State machine d p dp dp

preface:

State machine d p dp dp is mainly used to establish AC automata, open the state of one-dimensional AC automata, count or calculate the maximum value on AC automata, etc. Generally, memorization can be used to write, but memorization cannot compress space or optimize (such as matrix optimization). This can only be written recursively. During recursion, pay attention to setting the initial state and illegal state.

Generally, there are such restrictions on finding answers in the state machine

A string that does not contain a template string, that is, it is marked on the AC automata that the state does not transfer when it encounters this state.

For example: G - Censored!J - DNA repairE - DNA Sequence

If some pattern strings are included, we need to open one dimension to record their selection. Sometimes we need shape pressure, or we can use the total number - the number of pattern strings not included.

For example: H - Wireless PasswordN - Walk Through SquaresF - the boundless road to postgraduate entrance examination - word complex

G - Censored! (count + large number)

n,m,p. n characters, p strings, take M steps to find the scheme without strings.
Idea:

Run an ac automaton, disable string marking, and then d p dp dp , can be skipped.

#include <iostream>
#include <cstring>
#include <queue>
#include <vector>
#include <map>
using namespace std;
typedef long long LL;
const int Base=1000;
class BigNum
{
    public:
        int num[100], len;
        BigNum():len(0) {}
        BigNum(int n):len(0)
        {
            for( ; n>0; n/=Base)
                num[len++]=n%Base;
        }
        BigNum Bigvalueof(LL n)
        {
            len=0;
            while(n)
            {
                num[len++]=n%Base;
                n/=Base;
            }
            return *this;
        }
        BigNum operator+(const BigNum& b)
        {
            BigNum c;
            int i, carry=0;
            for(i=0; i<this->len || i<b.len || carry>0; ++i)
            {
                if(i<this->len)
                    carry+=this->num[i];
                if(i<b.len)
                    carry+=b.num[i];
                c.num[i]=carry%Base;
                carry/=Base;
            }
            c.len=i;
            return c;
        }
        BigNum operator +=(const BigNum& b)
        {
            *this=*this+b;
            return *this;
        }
        void Print()
        {
            if(len==0)
            {
                puts("0");
                return ;
            }
            printf("%d", num[len-1]);
            for(int i=len-2; i>=0; --i)
            {
                for(int j=Base/10; j>0; j/=10)
                {
                    printf("%d", num[i]/j%10);
                }
            }
            puts("");
        }
};
int n,m,k,tot;
int fail[110],col[110],tr[110][60];
char buf[60];
char ta[60];
map <char,int> ma;
BigNum f[60][110];
void insert(char s[],int sz){
    int p=0;
    for(int i=0;i<sz;i++){
        int ch=ma[s[i]];
        if(!tr[p][ch]) tr[p][ch]=++tot;
        p=tr[p][ch];

    }
    col[p]=1;
}

void build(){
    queue <int> q;
    for(int i=1;i<=n;i++){
        if(tr[0][i]) q.push(tr[0][i]);
    }
    while(!q.empty()){
        int p=q.front();q.pop();
        for(int i=1;i<=n;i++){
            if(tr[p][i]){
                fail[tr[p][i]]=tr[fail[p]][i];
                q.push(tr[p][i]);
            }else{
                tr[p][i]=tr[fail[p]][i];
            }
        }
        col[p]|=col[fail[p]];
    }
}
int main(){
    scanf("%d %d %d",&n,&m,&k);
    scanf("%s",ta+1);
    for(int i=1;i<=n;i++) ma[ta[i]]=i;
    for(int i=1;i<=k;i++){
        scanf("%s",buf);
        insert(buf,strlen(buf));
    }
    build();
    for(int i=0;i<=m;i++){
        for(int j=0;j<=tot;j++) f[i][j].Bigvalueof(0);
    }
    f[0][0].Bigvalueof(1);
    //f[0][0].push_back(1);
    for(int i=0;i<m;i++){
        for(int j=0;j<=tot;j++){
            for(int k=1;k<=n;k++){
                int z=tr[j][k];
                if(col[z]) continue;
                f[i+1][z]=f[i+1][z]+f[i][j];
            }
        }
    }
    BigNum res(0);
    for(int i=0;i<=tot;i++){
        if(col[i]) continue;
        res=res+f[m][i];
    }
    res.Print();

}

H - Wireless Password (count + pressure)

Question meaning: give you m strings. How many strings with length of n contain exactly k strings in M strings, which can be overlapped
Idea:

Since you want to include k of m strings, we need to press the selection of m strings to one dimension d p dp dp, then run the conventional ac automata, select the situation mark at each position of the ac automata, and then count dp.

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int mod=20090717;
int n,m,k,tot;
int col[110],tr[110][26],fail[110];
int f[30][105][1100];
char buf[15];
void init(){

    for(int i=0;i<=tot;i++){
        col[i]=fail[i]=0;
        memset(tr[i],0,sizeof tr[i]);
    }
    tot=0;
    memset(f,-1,sizeof f);
}
void insert(char s[],int sz,int x){
    int p=0;
    for(int i=0;i<sz;i++){
        int ch=s[i]-'a';
        if(!tr[p][ch]) tr[p][ch]=++tot;
        p=tr[p][ch];
    }
    col[p]=col[p]|(1<<x);
}
void build(){
    queue <int> q;
    for(int i=0;i<26;i++){
        if(tr[0][i]) q.push(tr[0][i]);
    }
    while(!q.empty()){
        int p=q.front();q.pop();
        for(int i=0;i<26;i++){
            if(tr[p][i]){
                fail[tr[p][i]]=tr[fail[p]][i];
                q.push(tr[p][i]);
            }else{
                tr[p][i]=tr[fail[p]][i];
            }
        }
        col[p]|=col[fail[p]];
    }
}
int check(int x){
    int sum=0;
    while(x){
        sum=sum+(x&1);
        x>>=1;
    }
    return sum>=k;
}
int dfs(int p,int s,int st){

    if(p>n){
        if(check(st)) return 1;
        return 0;
    }
    if(f[p][s][st]!=-1) return f[p][s][st];
    int ans=0;
    for(int i=0;i<26;i++){
        ans=(ans+dfs(p+1,tr[s][i],st|col[tr[s][i]]))%mod;
    }
    return f[p][s][st]=ans;
}
void solve(){
    init();
    for(int i=1;i<=m;i++){
        scanf("%s",buf);
        insert(buf,strlen(buf),i-1);
    }
    build();
    printf("%d\n",dfs(1,0,0));
}
int main(){
    while(scanf("%d %d %d",&n,&m,&k)&&(n||m||k)) solve();

}

I - Ring

Meaning:

Given m strings and the value of each string, now let you find a string with a length of no more than n to maximize its value. If multiple strings are satisfied, the shortest of all strings is output. If multiple strings are still satisfied, the string with the smallest dictionary order is output.

Idea:

Run the ac automata, mark the value of each point on the automata, and finally find a max on the dp side. Since the length is no more than n, you need to start from [ 1 , n ] [1,n] [1,n] all run to one side, or open another dimension, and it is OK to return every time.

code:
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N=55,M=1200;
int n,m,tot,T,w[110];
int f[N][M];
int tr[M][26],fail[M],col[M];
char buf[110][20];
void init(){
    for(int i=0;i<=tot;i++){
        memset(tr[i],0,sizeof tr[i]);
        fail[i]=col[i]=0;
    }
    memset(f,-1,sizeof f);
    tot=0;
}
void insert(char s[],int sz,int v){
    int p=0;
    for(int i=0;i<sz;i++){
        int ch=s[i]-'a';
        if(!tr[p][ch]) tr[p][ch]=++tot;
        p=tr[p][ch];
    }
    col[p]=v;
}
void build(){
    queue <int> q;
    for(int i=0;i<26;i++){
        if(tr[0][i]) q.push(tr[0][i]);
    }
    while(!q.empty()){
        int p=q.front();q.pop();
        for(int i=0;i<26;i++){
            if(tr[p][i]){
                fail[tr[p][i]]=tr[fail[p]][i];

                q.push(tr[p][i]);
            }else{
                tr[p][i]=tr[fail[p]][i];
            }
        }
        col[p]=col[p]+col[fail[p]];
    }
}
int dfs(int p,int s){
    if(p>n){
        return 0;
    }
    if(f[p][s]!=-1) return f[p][s];
    int ans=0;
    for(int i=0;i<26;i++){
        int v=tr[s][i];
        ans=max(ans,dfs(p+1,v)+col[v]);
    }
    return f[p][s]=ans;
}
void print(int p,int s){
    if(p>n) return;
    for(int i=0;i<26;i++){
        int v=tr[s][i];
        if(f[p][s]==dfs(p+1,v)+col[v]){
            printf("%c",'a'+i);
            print(p+1,v);
            break;
        }
    }
}
void solve(){
    scanf("%d %d",&n,&m);
    init();
    for(int i=1;i<=m;i++){
        scanf("%s",buf[i]);
    }
    for(int i=1;i<=m;i++){
        scanf("%d",&w[i]);
        insert(buf[i],strlen(buf[i]),w[i]);
    }
    build();
    int ans=dfs(1,0);
    //cout<<"ans="<<ans<<"\n";
    if(ans==0){
        puts("");
        return;
    }
    int idx=0;
    for(int i=n;i>=1;i--){
        if(dfs(i,0)==ans){
            idx=i;
            break;
        }
    }

    print(idx,0);
    puts("");

}
int main(){
    scanf("%d",&T);
    while(T--) solve();
}

J - DNA repair

Meaning:

There are n pattern strings and a target string. If you want to modify the target string so that there is no pattern string in the target string, ask how many characters to modify at least.

Idea:

Run ac automata, mark, then run dp, and be marked to skip

code:
#include <iostream>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=55;
const int inf=0x3f3f3f3f;
int n,m,o,tot,ma[500];
int tr[1100][4],col[1100],fail[1100];
int f[5][1100];
char buf[N],a[1010];
void init(){
    ma['A']=0,ma['G']=1,ma['C']=2,ma['T']=3;
    for(int i=0;i<=tot;i++){
        memset(tr[i],0,sizeof tr[i]);
        col[i]=fail[i]=0;
    }
    memset(f,inf,sizeof f);
    tot=0;
}
void insert(char s[],int sz){
    int p=0;
    for(int i=0;i<sz;i++){
        int ch=ma[s[i]];
        if(!tr[p][ch]) tr[p][ch]=++tot;
        p=tr[p][ch];
    }
    col[p]=1;
}
void build(){
    queue <int> q;
    for(int i=0;i<4;i++){
        if(tr[0][i]) q.push(tr[0][i]);
    }
    while(!q.empty()){
        int p=q.front();q.pop();
        for(int i=0;i<4;i++){
            if(tr[p][i]){
                fail[tr[p][i]]=tr[fail[p]][i];
                q.push(tr[p][i]);
            }else{
                tr[p][i]=tr[fail[p]][i];
            }
            col[p]|=col[fail[p]];
        }
    }

}
void solve(){
    init();
    for(int i=1;i<=n;i++){
        scanf("%s",buf);
        insert(buf,strlen(buf));
    }
    scanf("%s",a+1);
    m=strlen(a+1);
    build();
    f[0&1][0]=0;
    for(int i=0;i<m;i++){
        for(int j=0;j<=tot;j++) f[i+1&1][j]=inf;
        for(int j=0;j<=tot;j++){
            for(int k=0;k<4;k++){
                int z=tr[j][k];
                if(col[z]) continue;
                f[i+1&1][z]=min(f[i+1&1][z],f[i&1][j]+(k!=ma[a[i+1]]));
            }
        }
    }
    int ans=inf;
    for(int i=0;i<=tot;i++){
        ans=min(ans,f[m&1][i]);
    }

    if(ans==inf) cout<<"Case "<<++o<<": "<<"-1\n";
    else cout<<"Case "<<++o<<": "<<ans<<"\n";

}
int main(){
    while(scanf("%d",&n)&&n) solve();

}

L - Lost's revenge

Meaning:

Given an n-mode string and a target string, ask how many mode strings can be generated by reordering the target string, which can be overlapped, and all strings contain only A C G T.

Idea:

First run the ac automata, and then mark it on the ac automata. Due to the arrangement of the target string asked by the topic, count the number of target string ACGT, and then.

Set: d p [ s ] [ a ] [ b ] [ c ] [ d ] dp[s][a][b][c][d] dp[s][a][b][c][d] is in a c ac ac automata s state, a b c d abcd abcd are A C G T ACGT The selection of ACGT , and then memory search.

p s : ps: ps: because of the title a b c d abcd The number and of abcd are 40 40 40, so hold a meeting directly M L E MLE MLE, need to a b c d abcd abcd # status class is hash processing. See the code for specific processing.

code:
#include <cstdio>
#include <algorithm>
#include <queue>
#include <stack>
#include <string>
#include <string.h>
#include <map>
#include <iostream>
using namespace std;
const int maxn = 550;
const int mod = 20090717;
int INF = 1e9;
int nex[maxn][4], Exits[maxn], fail[maxn], last[maxn], cnt;
int num0, num1, num2, num3;
char t[maxn];
int n, m;
int getId(char ch){
	if(ch == 'A') return 0;
	else if(ch == 'T') return 1;
	else if(ch == 'C') return 2;
	else return 3;
}
void insert(char *s, int len){
	int p = 0;
	for(int i = 0; i < len; i++){
		int x = getId(s[i]);
		if(nex[p][x] == 0){
			memset(nex[cnt], 0, sizeof(nex[cnt]));
			Exits[cnt] = 0;
			last[cnt] = 0;
			fail[cnt] = 0;
			nex[p][x] = cnt++;
		}
		p = nex[p][x];
	}
	Exits[p]++;
}

queue<int> que;
void Build(){
	for(int i = 0; i < 4; i++){
		if(nex[0][i]) que.push(nex[0][i]);
	}
	while(que.size()){
		int p = que.front();
		que.pop();
		Exits[p] += Exits[fail[p]];
		for(int i = 0; i < 4; i++){
			int u = nex[p][i];
			if(u){
				fail[u] = nex[fail[p]][i];
				last[u] = Exits[fail[u]] ? fail[u] : last[fail[u]];
				que.push(u);
			} else {
				nex[p][i] = nex[fail[p]][i];
			}
		}
	}
}

int getState(int c0, int c1, int c2, int c3){
	return c0 * (num1 + 1) * (num2 + 1) * (num3 + 1) + c1 * (num2 + 1) * (num3 + 1) + c2 * (num3 + 1) + c3;
}
int dp[505][15005];
int dfs(int st, int c0, int c1, int c2, int c3){
	int state = getState(c0, c1, c2, c3);
	if(dp[st][state] != -1) return dp[st][state];
	int ans = 0;
	if(c0){
		int u = nex[st][0];
		ans = max(ans, dfs(u, c0 - 1, c1, c2, c3) + Exits[u]);
	}
	if(c1){
		int u = nex[st][1];
		ans = max(ans, dfs(u, c0, c1 - 1, c2, c3) + Exits[u]);
	}
	if(c2){
		int u = nex[st][2];
		ans = max(ans, dfs(u, c0, c1, c2 - 1, c3) + Exits[u]);
	}
	if(c3){
		int u = nex[st][3];
		ans = max(ans, dfs(u, c0, c1, c2, c3 - 1) + Exits[u]);
	}
	dp[st][state] = ans;
	return ans;
}
int main(int argc, char const *argv[])
{
	int ca = 0;
	while(1){
		scanf("%d", &n);
		if(n == 0) break;
		cnt = 1;
		memset(nex[0], 0, sizeof(nex[0]));
		num0 = num1 = num2 = num3 = 0;
		for(int i = 1; i <= n; i++){
			char s[25];
			scanf("%s", s);
			insert(s, strlen(s));
		}
		scanf("%s", t);
		int len = strlen(t);
		for(int i = 0; i < len; i++){
			if(getId(t[i]) == 0) num0++;
			else if(getId(t[i]) == 1) num1++;
			else if(getId(t[i]) == 2) num2++;
			else num3++;
		}
		Build();
		memset(dp, -1, sizeof(dp));
		int ans = dfs(0, num0, num1, num2, num3);
		printf("Case %d: %d\n", ++ca, ans);
	}
    return 0;
}

N - Walk Through Squares

Meaning:

In a matrix, go from the upper left corner to the lower right corner, go right to get an R, go down to get a D, and ask how many strings you can get when you go to the lower right corner, including the two strings given in the question

Idea:

Run ac automata, mark, and then open one-dimensional pressure to check its selection.

code:
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
typedef long long ll;
const int mod=1000000007;
int T,n,m,tot;
int tr[210][2],fail[210],col[210];
int f[105][105][205][5];
char buf[110];
void init(){
    for(int i=0;i<=tot;i++){
        memset(tr[i],0,sizeof tr[i]);
        fail[i]=col[i]=0;
    }
    tot=0;
}
int get(char ch){
    if(ch=='R') return 0;
    else return 1;
}
void insert(char s[],int sz,int id){
    int p=0;
    for(int i=0;i<sz;i++){
        int ch=get(s[i]);
        if(!tr[p][ch]) tr[p][ch]=++tot;
        p=tr[p][ch];
    }
    col[p]=(1<<id);
}
void build(){
    queue <int> q;
    for(int i=0;i<2;i++){
        if(tr[0][i]) q.push(tr[0][i]);
    }
    while(!q.empty()){
        int p=q.front();q.pop();
        for(int i=0;i<2;i++){
            if(tr[p][i]){
                fail[tr[p][i]]=tr[fail[p]][i];
                q.push(tr[p][i]);
            }else{
                tr[p][i]=tr[fail[p]][i];
            }
        }
        col[p]|=col[fail[p]];
    }
}
int dfs(int a,int b,int s,int lim){
    if(a==0&&b==0){
        if(lim==(1<<2)-1) return 1;
        return 0;
    }
    if(f[a][b][s][lim]!=-1) return f[a][b][s][lim];
    ll ans=0;
    for(int i=0;i<2;i++){
        int j=tr[s][i];
        if(i==0&&a){
            ans=(ans+dfs(a-1,b,j,lim|col[j]))%mod;
        }else if(i==1&&b){
            ans=(ans+dfs(a,b-1,j,lim|col[j]))%mod;
        }
    }
    return f[a][b][s][lim]=ans;
}
void solve(){
    scanf("%d %d",&n,&m);
    init();
    for(int i=0;i<2;i++){
        scanf("%s",buf);
        insert(buf,strlen(buf),i);
    }
    build();
    for(int i=0;i<=n+1;i++){
        for(int j=0;j<=m+1;j++){
            for(int k=0;k<=tot;k++){
                f[i][j][k][0]=f[i][j][k][1]=f[i][j][k][2]=-1;
                f[i][j][k][3]=-1;
            }
        }
    }
    printf("%d\n",dfs(n,m,0,0));
}
int main(){
    scanf("%d",&T);
    while(T--) solve();
}

E - DNA Sequence

Meaning:

There are m kinds of DNA sequences that are pathogenic. How many kinds of DNA are n long and do not contain pathogenic sequences

Idea:

Build ac automata to mark the disease.

Set state f [ i ] [ j ] f[i][j] f[i][j] is length i i The string of i , is in the state machine j j Point j does not contain the DNA type of pathogenic sequence.

Transfer: f [ i ] [ k ] + = f [ i ] [ j ] f[i][k]+=f[i][j] f[i][k]+=f[i][j], and k = t r [ j ] [ c ] k=tr[j][c] k=tr[j][c], which contains the pathogenic string without metastasis. ​

We see that n is very large. In this problem, we include pathogenic sequences. We can use matrix to recursively optimize, similar to counting the number of paths.

We need to create a matrix according to the AC automatic mechanism (the class is to construct the adjacency matrix), and finally count the answer of the fast power of the matrix.

#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll mod=100000;
const int N=101;
int m,n,tot,ma[500],col[N],tr[N][4],fail[N];
ll A[N][N];
char buf[N];
void qu(ll &x){
    x=x-x/mod*mod;
}
void mul(ll c[],ll a[],ll b[][N]){
    ll t[N]={0};
    for(int i=0;i<=tot;i++){
        for(int j=0;j<=tot;j++){
            t[i]=t[i]+a[j]*b[j][i];
        }
        //qu(t[i]);
        t[i]=t[i]%mod;
    }
    memcpy(c,t,sizeof t);
}
void mul(ll c[][N],ll a[][N],ll b[][N]){
    ll t[N][N]={0};
    for(int i=0;i<=tot;i++){
        for(int j=0;j<=tot;j++){
            for(int k=0;k<=tot;k++){
                t[i][j]=t[i][j]+a[i][k]*b[k][j];
            }
            //qu(t[i][j]);
            t[i][j]=t[i][j]%mod;
        }
    }
    memcpy(c,t,sizeof t);
}

ll ksm(ll p){
    ll F0[N]={1};
    while(p){
        if(p&1) mul(F0,F0,A);
        mul(A,A,A);
        p>>=1;
    }
    ll ans=0;
    for(int i=0;i<=tot;i++){
        //if(!col[i])
        ans=ans+F0[i];
    }
    //qu(ans);
    ans=ans%mod;
    return ans;
}

void insert(char s[],int sz){
    int p=0;
    for(int i=0;i<sz;i++){
        int ch=ma[s[i]];
        if(!tr[p][ch]) tr[p][ch]=++tot;
        p=tr[p][ch];
    }
    col[p]++;
}

void build(){
    queue <int> q;
    for(int i=0;i<4;i++){
        if(tr[0][i]) q.push(tr[0][i]);
    }
    while(!q.empty()){
        int p=q.front();q.pop();
        for(int i=0;i<4;i++){
            if(tr[p][i]){
                fail[tr[p][i]]=tr[fail[p]][i];
                q.push(tr[p][i]);
            }else{
                tr[p][i]=tr[fail[p]][i];
            }
        }
        col[p]+=col[fail[p]];
    }
}

int main(){
    scanf("%d %d",&m,&n);
    ma['A']=0,ma['C']=1,ma['T']=2,ma['G']=3;
    for(int i=1;i<=m;i++){
        scanf("%s",buf);
        insert(buf,strlen(buf));
    }
    build();
    for(int i=0;i<=tot;i++){
        if(col[i]) continue;
        for(int j=0;j<4;j++){
            if(col[tr[i][j]]) continue;
            A[i][tr[i][j]]++;
        }
    }

    printf("%lld\n",ksm(n));

}

F - the boundless road to postgraduate entrance examination - word complex

Meaning:

Given n Template Strings, ask how many strings with at least one template string whose length does not exceed L.

Idea:

This question is as n large as the previous one, but it contains at least one template string quantity. If we calculate the quantity from the front, it will be a little complicated. We can consider calculating the total quantity from the back - excluding the pattern string

If the length is L and the number of template strings is not included, it is the same as the above question,

However, what we find here is the sum of the number of template strings with a length of [1,L], which we can set S i S_i Si = and [ 1 , i ] [1,i] The sum of [1,i], i.e S i = f [ 0 ] + f [ 1 ] + f [ 2 ] . . + f [ i ] + s [ i − 1 ] S_i=f[0]+f[1]+f[2]..+f[i]+s[i-1] Si = f[0]+f[1]+f[2]..+f[i]+s[i − 1], we can also find the fast power of the matrix, that is, in the last column, all one dimensions are 1

code:
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
typedef unsigned long long ll;
const int N=50;
int n,m,tot;
int col[N],fail[N],tr[N][26];
char buf[10];
struct node{
    ll e[N][N];
    int n;
    node(int _){
        n=_;
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                e[i][j]=0;
            }
        }
    }
    void build(){
        for(int i=0;i<n;i++){
            e[i][i]=1;
        }
    }
};
node operator * (const node& x,const node& y){
    node res=node(x.n);
    for(int i=0;i<res.n;i++){
        for(int j=0;j<res.n;j++){
            res.e[i][j]=0;
            for(int k=0;k<res.n;k++){
                res.e[i][j]=res.e[i][j]+x.e[i][k]*y.e[k][j];
            }
        }
    }
    return res;
}
void init(){
    memset(col,0,sizeof col);
    memset(fail,0,sizeof fail);
    memset(tr,0,sizeof tr);
    tot=0;
}
void insert(char s[],int sz){
    int p=0;
    for(int i=0;i<sz;i++){
        int ch=s[i]-'a';
        if(!tr[p][ch]) tr[p][ch]=++tot;
        p=tr[p][ch];
    }
    col[p]++;
}
void build(){
    queue <int> q;
    for(int i=0;i<26;i++){
        if(tr[0][i]) q.push(tr[0][i]);
    }
    while(!q.empty()){
        int p=q.front();q.pop();
        for(int i=0;i<26;i++){
            if(tr[p][i]){
                fail[tr[p][i]]=tr[fail[p]][i];
                q.push(tr[p][i]);
            }else{
                tr[p][i]=tr[fail[p]][i];
            }
        }
        col[p]+=col[fail[p]];
    }

}
ll ksm(node A,int p){
    node F=node(A.n);
    F.build();
    while(p){
        if(p&1) F=F*A;
        A=A*A;
        p>>=1;
    }

    ll res=0;
    for(int i=0;i<F.n;i++){
        res=res+F.e[0][i];
    }
    return res;
}
ll ks(node AB,int p){
    node F=node(2);
    F.e[0][0]=26,F.e[0][1]=1;
    while(p){
        if(p&1) F=F*AB;
        AB=AB*AB;
        p>>=1;
    }
    return F.e[0][0];
}

void solve(){
    init();
    for(int i=1;i<=n;i++){
        scanf("%s",buf);
        insert(buf,strlen(buf));
    }
    build();
    node A=node(tot+2);
    for(int i=0;i<A.n-1;i++){
        if(col[i]) continue;
        for(int c=0;c<26;c++){
            int  k=tr[i][c];
            if(col[k]) continue;
            A.e[i][k]++;
        }
    }
    for(int i=0;i<A.n;i++){
        A.e[i][A.n-1]=1;
    }
    node AB=node(2);
    AB.e[0][0]=AB.e[1][0]=26;
    AB.e[1][1]=1;
    //cout<<ks(AB,m-1)<<" "<<(ksm(A,m)-1)<<" "<<m<<"\n";
    printf("%llu\n",ks(AB,m-1)-(ksm(A,m)-1));
}
int main(){
    while(scanf("%d %d",&n,&m)!=EOF) solve();
}

shortest path

O - Xiaoming series stories - girlfriend's test (DAG + shortest)

Meaning:

Let you find the shortest path from 1 to n, but some paths can't go, and you can only go to a point larger than the current point each time

Idea:

First, build the points that can't run a c ac ac , automata tag, because it is D A G DAG DAG, we can memorize the shortest path, and then run the map while running a c ac ac automata, encounter a c ac The points marked on ac automata are skipped.

code:
#include <iostream>
#include <cstring>
#include <cmath>
#include <queue>
#include <algorithm>
#define bug cout<<"....\n"
#define fi first
#define se second
using namespace std;
typedef pair <int,int> pll;
typedef pair <double,pll> pdd;
const int N=55,M=510;
int n,m,tot;
int buf[N],tr[M][N],fail[M],col[M];
int vis[N],state[N];
double x[N],y[N],dis[N],dp[N][M];
void init(){
    for(int i=0;i<=tot;i++){
        memset(tr[i],0,sizeof tr[i]);
        fail[i]=col[i]=0;
    }
    for(int i=0;i<N;i++){
        for(int j=0;j<M;j++){
            dp[i][j]=-1;
        }
    }
    tot=0;
}
double gao(int i,int j){
    return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}
void insert(int s[],int sz){
    int p=0;
    for(int i=0;i<sz;i++){
        int ch=s[i];
        if(!tr[p][ch]) tr[p][ch]=++tot;
        p=tr[p][ch];
    }
    col[p]=1;
}
int build(){
    queue <int> q;
    for(int i=1;i<=50;i++){
        if(tr[0][i]) q.push(tr[0][i]);
    }
    while(!q.empty()){
        int p=q.front();q.pop();
        for(int i=1;i<=50;i++){
            if(tr[p][i]){
                fail[tr[p][i]]=tr[fail[p]][i];
                q.push(tr[p][i]);
            }else{
                tr[p][i]=tr[fail[p]][i];
            }
        }
        col[p]|=col[fail[p]];
    }
}
double dfs(int p,int s){
    if(p==n){
        return 0;
    }
    if(dp[p][s]!=-1) return dp[p][s];
    double ans=1e18;
    for(int i=p+1;i<=n;i++){
        double w=gao(p,i);
        if(col[tr[s][i]]) continue;
        ans=min(ans,dfs(i,tr[s][i])+w);
    }
    return dp[p][s]=ans;
}
void solve(){
    init();
    for(int i=1;i<=n;i++){
        scanf("%lf %lf",&x[i],&y[i]);
    }
    for(int i=1;i<=m;i++){
        int sz;
        scanf("%d",&sz);
        for(int j=0;j<sz;j++){
            scanf("%d",&buf[j]);
        }
        insert(buf,sz);
    }
    build();
    if(col[tr[0][1]]){
        puts("Can not be reached!");
        return;
    }
    double ans=dfs(1,tr[0][1]);
    if(ans==1e18){
        puts("Can not be reached!");
    }else{
        printf("%.2f\n",ans);
    }
}
int main(){
    while(scanf("%d %d",&n,&m)&&(n||m)) solve();
}

M - Resource Archiver

Meaning:

Here you are n n n resource strings and m m m virus strings, and then let you find the minimum number 01 01 01 character can make a string without virus string and containing all resource strings.

Idea:

Note the number of resource strings ( n ≤ 10 ) (n\le10) (n ≤ 10) is relatively small. We can build AC automata first, run bfs for each resource string on the established AC automata, skip the virus string, and process the distance between the two resource strings.

Final pressure d p dp dp, figure out what happened n n The shortest path of n strings (class is Hamiltonian graph).

code:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int N=15,M=60005;
int n,m,tot;
int tr[M][2],ban[M],num[N],fail[M];
int dis[M],vis[M],c[N][N];
int f[15][1<<12];
char buf[50005];
void init(){
    for(int i=0;i<=tot;i++){
        memset(tr[i],0,sizeof tr[i]);
        ban[i]=num[i]=fail[i]=0;
    }
    tot=0;
    memset(c,0,sizeof c);
    memset(f,-1,sizeof f);
}
void insert(char s[],int sz,int id){
    int p=0;
    for(int i=0;i<sz;i++){
        int ch=s[i]-'0';
        if(!tr[p][ch]) tr[p][ch]=++tot;
        p=tr[p][ch];
    }
    num[id]=p;
}
void add(char s[],int sz){
    int p=0;
    for(int i=0;i<sz;i++){
        int ch=s[i]-'0';
        if(!tr[p][ch]) tr[p][ch]=++tot;
        p=tr[p][ch];
    }
    ban[p]=1;
}
void build(){
    queue <int> q;
    for(int i=0;i<2;i++){
        if(tr[0][i]) q.push(tr[0][i]);
    }
    while(!q.empty()){
        int p=q.front();q.pop();
        for(int i=0;i<2;i++){
            if(tr[p][i]){
                fail[tr[p][i]]=tr[fail[p]][i];
                q.push(tr[p][i]);
            }else{
                tr[p][i]=tr[fail[p]][i];
            }
        }
        ban[p]|=ban[fail[p]];
    }
}
void bfs(int k){
    queue <int> q;
    for(int i=0;i<=tot;i++) vis[i]=dis[i]=0;
    q.push(num[k]),vis[num[k]]=1;
    while(!q.empty()){
        int p=q.front();q.pop();
        for(int i=0;i<2;i++){
            int v=tr[p][i];
            if(!vis[v]&&!ban[v]){
                vis[v]=1;
                dis[v]=dis[p]+1;
                q.push(tr[p][i]);
            }
        }
    }
    for(int i=0;i<=n;i++) c[k][i]=dis[num[i]];
}

int dfs(int p,int s){
    if(s==((1<<(n+1))-1)) return 0;
    if(f[p][s]!=-1) return f[p][s];
    int ans=1e9;
    for(int i=1;i<=n;i++){
        if(s&(1<<i)) continue;
        ans=min(ans,dfs(i,s|(1<<i))+c[p][i]);
    }
    return f[p][s]=ans;
}
void solve(){
    init();
    for(int i=1;i<=n;i++){
        scanf("%s",buf);
        insert(buf,strlen(buf),i);
    }
    for(int i=1;i<=m;i++){
        scanf("%s",buf);
        add(buf,strlen(buf));
    }
    build();
    for(int i=0;i<=n;i++){
        bfs(i);
    }
    printf("%d\n",dfs(0,1));
}
int main(){
    while(scanf("%d %d",&n,&m)&&(n||m)) solve();
}

Multimode string matching:

Meaning:

Give you a word list with a length of n, a text string, and ask you how many words appear in the word list in this text string;

Idea:

Build AC automata and put the text string into the matching. Remember to mark the matched points and calculate the answer only once.

code:
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
const int N=10005,M=1000007;
int n,tot,T,col[M],fail[M],tire[M][26];
char a[N][100],b[N];
void init(){
    for(int i=0;i<=tot;i++){
        fail[i]=col[i]=0;
        memset(tire[i],0,sizeof tire[i]);
    }
    tot=0;
}
void insert(char s[],int sz){
    int p=0;
    for(int i=0;i<sz;i++){
        int ch=s[i]-'a';
        if(!tire[p][ch]) tire[p][ch]=++tot;
        p=tire[p][ch];
    }
    //?
    col[p]++;
}
void build(){
    queue <int> q;
    for(int i=0;i<26;i++){
        if(tire[0][i]){
            fail[tire[0][i]]=0;
            q.push(tire[0][i]);
        }
    }
    while(!q.empty()){
        int p=q.front();q.pop();
        for(int i=0;i<26;i++){
            if(tire[p][i]){
                fail[tire[p][i]]=tire[fail[p]][i];
                q.push(tire[p][i]);
            }else{
                tire[p][i]=tire[fail[p]][i];
            }
        }
    }
}
int ask(char s[],int sz){
    int p=0,ans=0;
    for(int i=0;i<sz;i++){
        int ch=s[i]-'a';
        p=tire[p][ch];
        for(int j=p;j&&col[j]!=-1;j=fail[j]){
            ans+=col[j];
            col[j]=-1;
        }
    }
    return ans;
}


int main(){
    scanf("%d",&T);
    while(T--){

        scanf("%d",&n);
        init();
        for(int i=1;i<=n;i++){
            scanf("%s",a[i]);
            insert(a[i],strlen(a[i]));
        }
        scanf("%s",b);
        build();
        printf("%d\n",ask(b,strlen(b)));
    }

}

B - virus invasion

Meaning:

Give the string of N viruses, and then give the string of M websites. Find the number of viruses in the string and the URL containing viruses in total,

Idea:

Build the virus string into AC automata, and then run the website to AC automata for matching. Remember to mark the matched points each time and calculate the answer only once.

code:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
#define fi first
#define se second
#Define bug cout < < ha? \ n
using namespace std;
typedef pair <int,int> pll;
int n,m,tot;
int fail[100007],col[100007],tire[100007][128];
char buf[10010];
vector <int> ans;
vector <pll> d;
void insert(char s[],int sz,int id){
    int p=0;
    for(int i=0;i<sz;i++){
        int ch=s[i];
        if(!tire[p][ch]) tire[p][ch]=++tot;
        p=tire[p][ch];
    }
    col[p]=id;
}
void build(){
    queue <int> q;
    for(int i=0;i<128;i++){
        if(tire[0][i]){
            fail[tire[0][i]]=0;
            q.push(tire[0][i]);
        }
    }
    while(!q.empty()){
        int p=q.front();q.pop();
        for(int i=0;i<128;i++){
            if(tire[p][i]){
                fail[tire[p][i]]=tire[fail[p]][i];
                q.push(tire[p][i]);
            }else{
                tire[p][i]=tire[fail[p]][i];
            }
        }
    }
}
void query(char s[],int sz){
    int p=0;
    for(int i=0;i<sz;i++){
        int ch=s[i];
        p=tire[p][ch];
        for(int j=p;j&&col[j]!=-1;j=fail[j]){
            if(col[j]) ans.push_back(col[j]);
            d.push_back(pll(j,col[j]));
            col[j]=-1;
        }
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%s",buf);
        insert(buf,strlen(buf),i);
    }
    build();
    scanf("%d",&m);
    int res=0;
    for(int i=1;i<=m;i++){
        scanf("%s",buf);
        query(buf,strlen(buf));
        if(ans.size()){
            printf("web %d:",i);
            res++;
            sort(ans.begin(),ans.end());
            for(auto p:ans){
                printf(" %d",p);
            }
            puts("");
            for(auto it:d){
                col[it.fi]=it.se;
            }
            d.clear();
            ans.clear();
        }
    }
    printf("total: %d\n",res);
}

C - virus invasion continues

Meaning:

Give you n strings, then give you a long string, and ask you n strings, the number of times each string appears

Idea:

Take n strings to build AC automata, then match the long string, and record the occurrence times of each node during matching.

code:
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
typedef long long ll;
const int M=50007,N=2000007;
int n,tot;
int col[M],tire[M][128],fail[M],Ans[M],ma[M];
char a[N],buf[1007][100];
void init(){
    for(int i=0;i<=tot;i++){
        fail[i]=col[i]=Ans[i]=0;
        memset(tire[i],0,sizeof tire[i]);
    }
    for(int i=1;i<=n;i++) ma[i]=0;
   tot=0;
}
void insert(char s[],int sz,int id){
    int p=0;
    for(int i=0;i<sz;i++){
        int ch=s[i];
        if(!tire[p][ch]) tire[p][ch]=++tot;
        p=tire[p][ch];
    }
    ma[id]=p;
    col[p]++;
}
void build(){
    queue <int> q;
    for(int i=0;i<128;i++){
        if(tire[0][i]){
            fail[tire[0][i]]=0;
            q.push(tire[0][i]);
        }
    }
    while(!q.empty()){
        int p=q.front();q.pop();
        for(int i=0;i<128;i++){
            if(tire[p][i]){
                fail[tire[p][i]]=tire[fail[p]][i];
                q.push(tire[p][i]);
            }else{
                tire[p][i]=tire[fail[p]][i];
            }
        }

    }
}
void query(char s[],int sz){
    int p=0;
    for(int i=0;i<sz;i++){
        int ch=s[i];
        p=tire[p][ch];
        for(int j=p;j;j=fail[j]) Ans[j]+=col[j];
    }
}
int main(){
    while(scanf("%d",&n)!=EOF){
        init();
        for(int i=1;i<=n;i++){
            scanf("%s",buf[i]);
            insert(buf[i],strlen(buf[i]),i);
        }
        build();
        scanf("%s",a);
        query(a,strlen(a));
        for(int i=1;i<=n;i++){
            if(Ans[ma[i]]) printf("%s: %d\n",buf[i],Ans[ma[i]]);
        }
    }
}

ZOJ - 3228

Meaning:

Query a string s and n times, and query the number of times a string appears in s each time. Ord = 0 indicates that overlap is allowed, and ord = 1 indicates that overlap is not allowed.

Idea:

The first question is the same as the previous question. The second question does not allow overlap. We can record the position ed of the last match in the s string and the length len of the string. During each match, we can judge whether it overlaps with the last match according to ED and Len.

code:
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N=1e5+10;
int n,m,o,tot;
int len[6*N],ed[6*N],tr[6*N][26],fail[6*N];
int ans[2][6*N],tp[N],num[N];
char a[N],buf[10];
void init(){
    for(int i=0;i<=tot;i++){
        memset(tr[i],0,sizeof tr[i]);
        fail[i]=ed[i]=len[i]=0;
        ans[0][i]=ans[1][i]=0;
    }
    tot=0;
}
void insert(char s[],int sz,int id){
    int p=0;
    for(int i=0;i<sz;i++){
        int ch=s[i]-'a';
        if(!tr[p][ch]) tr[p][ch]=++tot;
        p=tr[p][ch];
        ed[p]=-1;
    }
    len[p]=sz;num[id]=p;
}
void build(){
    queue <int> q;
    for(int i=0;i<26;i++){
        if(tr[0][i]) q.push(tr[0][i]);
    }
    while(!q.empty()){
        int p=q.front();q.pop();
        for(int i=0;i<26;i++){
            if(tr[p][i]){
                fail[tr[p][i]]=tr[fail[p]][i];
                q.push(tr[p][i]);
            }else{
                tr[p][i]=tr[fail[p]][i];
            }
        }
    }
}
int query(char s[],int sz){
    int p=0;
    for(int i=0;i<sz;i++){
        int ch=s[i]-'a';
        p=tr[p][ch];
        for(int j=p;j;j=fail[j]){
            if(len[j]) ans[0][j]++;
            if(len[j]&&(i-ed[j]>=len[j])){
                ans[1][j]++;ed[j]=i;
            }
        }
    }
}
int main(){
    while(scanf("%s",a)!=EOF){
        scanf("%d",&m);
        init();
        for(int i=1;i<=m;i++){
            scanf("%d %s",&tp[i],buf);
            insert(buf,strlen(buf),i);
        }
        build();
        query(a,strlen(a));
        printf("Case %d\n",++o);
        for(int i=1;i<=m;i++){
            if(tp[i]==0) printf("%d\n",ans[0][num[i]]);
            else printf("%d\n",ans[1][num[i]]);
        }
        puts("");
    }
}

Tags: Algorithm Dynamic Programming

Posted on Tue, 21 Sep 2021 15:20:25 -0400 by madolia