2018北京icpc区域赛 H - Approximate Matching(AC自动机+DP)

String matching, a common problem in DNA sequence analysis and text editing, is to find the occurrences of one certain string (called pattern) in a larger string (called text). In some cases, the pattern is not required to be exactly in the text, and minor differences are acceptable (due to possible typing mistakes). When given a pattern string and a text string, we say pattern P is approximately matched within text S, if there is a substring of S which is at most one letter different from P. Note that the length of this substring and the pattern must be identical. For example, pattern “abb” is approximately matched in text “babc” but not matched in “bbac”.

It is easy to check if a pattern is approximately matched in a text. So your task is to count the number of all text strings of length m in which the given pattern can be approximately matched, and both of the patterns and texts are binary strings in order not to handle big integers.

Input
The first line of input is a single integer T (1 ≤ T ≤ 666), the number of test cases. Each test case begins with a line of two integers n,m (1 ≤ n,m ≤ 40), denoting the length of pattern string and text string. Then a single line of binary string P follows, which denotes the pattern. Note that there will be at most 15 test cases in which n ≥ 16.

Output
For each test case, output a single line with one integer, representing the answer.


题解:
近似匹配表示最多只能有一个字符不匹配,所以我们可以用源串s和它可能的近似匹配串都建立AC自动机,然后就是跑套路DP了-.-!

设dp[i][j]表示当前枚举到了第i个字符,在AC自动机上的j节点并且没有近似匹配的字符串的数量,因为题意求至少近似匹配一次,所以转换成反面会好做很多,求出没有近似匹配的字符串数量后,用2^m减去就好了

听说当时北京赛区5题能拿金(小声bb)


AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 1005;
int nxt[MAXN][2],fail[MAXN],vis[MAXN],tot;
LL dp[MAXN][MAXN],qmi[50];
char s[MAXN];
inline void Init(){
    for(int i=0;i<=tot;i++){
        fail[i]=vis[i]=0;
        for(int j=0;j<2;j++) nxt[i][j]=0;
    }
    tot=1;
}
inline void Insert(char *s){
    int rt=0,len=strlen(s);
    for(int i=0;i<len;i++){
        if(!nxt[rt][s[i]-'0']) nxt[rt][s[i]-'0']=++tot;
        rt=nxt[rt][s[i]-'0'];
    }
    vis[rt]=1;
}
inline void Build(){
    queue<int> que;
    for(int i=0;i<2;i++) if(nxt[0][i]) que.push(nxt[0][i]);
    while(!que.empty()){
        int u=que.front(); que.pop();
        for(int i=0;i<2;i++){
            if(nxt[u][i]) fail[nxt[u][i]]=nxt[fail[u]][i],que.push(nxt[u][i]);
            else nxt[u][i]=nxt[fail[u]][i];
        }
    }
}
inline char change(char ch){ return (ch-'0')^1+'0'; }
int main(){
    //freopen("C:\\Users\\Administrator\\Desktop\\in.txt","r",stdin);
    qmi[0]=1; for(int i=1;i<=45;i++) qmi[i]=qmi[i-1]*2;
    int T; scanf("%d",&T);
    while(T--){
        Init();
        int n,m; scanf("%d%d",&n,&m);
        scanf("%s",s);
        Insert(s);
        for(int i=0;i<n;i++){
            s[i]=change(s[i]);
            Insert(s);
            s[i]=change(s[i]);
        }
        Build();
        memset(dp,0,sizeof(dp));
        dp[0][0]=1;
        for(int i=1;i<=m;i++){
            for(int j=0;j<=tot;j++){
                if(vis[j]) continue;
                for(int k=0;k<2;k++){
                    if(vis[nxt[j][k]]) continue;
                    dp[i][nxt[j][k]] += dp[i-1][j];
                }
            }
        }
        LL res = qmi[m];
        for(int i=0;i<=tot;i++) res-=dp[m][i];
        printf("%lld\n",res);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值