hdu5716 hdu5745 shift-and字符串匹配

来源:互联网 发布:分卷包数据不正确修复 编辑:程序博客网 时间:2024/06/16 18:58

shift-and算法

2016大连regional的B题,字符串匹配,模板串每一位有若干种选择
一眼过去以为是KMP,写了之后一直超时,因为KMP会误配,使得不匹配的地方匹配,最后输出太多TLE
正解是shift-and,原来今年百度之星决赛和多校出过类似的题

hdu5716

题意:200W的文本串s是确定的,500的模板串t,每一位的字符在字符集当中,字符集最多包含大小写字母及数字,找出所有匹配点。


题解:n表示s的长度,m表示t的长度。dp[i][j]表示s[i-j+1..i]与t[1..j]是否相等
转移dp[i][j] = dp[i-1][j-1] && (s[i]∈t[j]),其中t[j]表示模板串第j位的字符集
这样复杂度是O(nm),考虑到i和j都是同步增加的,状态又是bool类型,可以用bitset优化


bitset优化有两种

方法1:dp[i]表示长度为n的bitset,dp[i][j]=1表示s到j这个位置和t匹配的长度可以为i
dp[i]=dp[i-1] << 1 & mt[t[i]]
mt[c]表示c这个字符可以和s的那些位置匹配
求mt[c]的复杂度是O(msizen64),size为字符集大小


方法2:dp[i]表示长度为m的bitset,dp[i][j]=1表示s以i结尾,和t匹配的长度可以为j
dp[i]=dp[i-1] << 1 & mt[s[i]]
mt[c]表示c这个字符可以和t的那些位置匹配
求mt[c]的复杂度是O(msizem64)


两种方法转移方程一样,dp的复杂度都是O(nm64),但是mt的计算量不同,导致效率不一样。
字符集大小对复杂度影响也比较大

#include <cstdio>#include <cstring>#include <cstdlib>#include <algorithm>#include <bitset>using namespace std;const int M = 500+5;const int N = 2000000+5;const int SIZE = 64;const int MAGIC = 128;char txt[N];int id[MAGIC];bitset<M> dp[2];bitset<M> mt[MAGIC];int n, m;inline void output(bitset<M>& bt){    for (int i=0; i<=m; i++)        printf("\t #  %4d %4d\n", i, bt[i]?1:0);    puts("");}int main(){    int cnt = 0;    for (int i='0'; i<='9'; i++) id[i] = ++ cnt;    for (int i='A'; i<='Z'; i++) id[i] = ++ cnt;    for (int i='a'; i<='z'; i++) id[i] = ++ cnt;    while (gets(txt+1)){        n = strlen(txt+1);        scanf("%d", &m);        int l; char s[SIZE];        for (int i=1; i<=cnt; i++) mt[i].reset();        for (int i=1; i<=m; i++){            scanf("%d %s", &l, s);            for (int j=0; j<l; j++){                int x = id[s[j]];                mt[x].set(i);            }        }        int cur = 0;        dp[cur].reset();        dp[cur].set(0);        bool flag = 0;        for (int i=1; i<=n; i++){            int x = id[txt[i]];            cur ^= 1;            dp[cur] = dp[cur^1] << 1 & mt[x];            dp[cur].set(0);            if (dp[cur][m]){                flag = 1;                printf("%d\n", i - m + 1);            }        }        if (!flag) puts("NULL");        getchar();    }    return 0;}

hdu5745

题意:10W的文本串s,5000的模板串t,其中t可以交换相邻的字符,但每个位置最多被交换一次,比如1和2交换,2和3就不能交换了,求出所有匹配点。


题解:类似上一题的做法,dp[i][j][0]表示s到i位置和t能否匹配j的长度,且j位置不能与后面交换(但可能跟前面交换,看作一种情况),dp[i][j][1]表示s到i位置和t能否匹配j的长度,且j位置与后面交换
转移方程为
dp[i][j][0] = (dp[i1][j1][1] & s[i]==t[j1]) | (dp[i1][j1][0] & s[i]==t[j])
dp[i][j][1] = (dp[i1][j1][0] & s[i]==t[j+1])


然后就是用bitset优化了,也是两种优化方式,预处理mt数组的复杂度也不一样

#include <cstdio>#include <cstring>#include <cstdlib>#include <algorithm>#include <bitset>using namespace std;const int N = 100000+10;const int M = 5000+5;const int SIZE = 26+2;const int MAGIC = 256;int n, m;char txt[N], pat[M];int s[N], t[M];bitset<N> dp[2][2];bitset<N> f[SIZE];bitset<N> ans;inline void output(bitset<N>& bt){    for (int i=1; i<=n; i++){        putchar(bt[i]?49:48);    }    putchar('\n');}int main(){    int test;    scanf("%d", &test);    while (test--){        scanf("%d %d", &n, &m);        scanf("%s", txt+1);        scanf("%s", pat+1);        for (int i=1; i<=n; i++) s[i] = txt[i] - 'a';        for (int i=1; i<=m; i++) t[i] = pat[i] - 'a';        t[m+1] = 26;        for (int i=0; i<SIZE; i++){            f[i].reset();        }        for (int i=1; i<=n; i++)            f[s[i]].set(i);        int cur = 0, pre;        dp[cur][0].set();        dp[cur][1].reset();        for (int i=1; i<=m; i++){            pre = cur; cur = pre ^ 1;            dp[cur][0] = (dp[pre][1] << 1 & f[t[i-1]]) | (dp[pre][0] << 1 & f[t[i]]);            dp[cur][1] = (dp[pre][0] << 1 & f[t[i+1]]);        }        ans = dp[cur][0] | dp[cur][1];        for (int i=1; i<=n; i++){            if (i+m-1<=n && ans[i+m-1]) putchar('1');            else putchar('0');        }        putchar('\n');    }    return 0;}

有意思的是上面的题需要用方法2,下面的题需要用方法1,不然tle

0 0
原创粉丝点击