更高效的字符串匹配算法——shift-and

在接触这个算法之前,一直觉得kmp巧夺天工,利用next数组的递推,实现对于模式串任一子串最大相同前后缀的找寻,继而在匹配目标串的过程中,一旦遇到失配情况,可以令 匹配起始下标 进行合理范围内最大的跳跃,从而将匹配整体复杂度从O(nm)降为O(m+n)。

a b c a b c ........

a b c a b    可从目标串第二个 a 处开始匹配

shift-and算法与其思路基本相同,同样是找寻最大匹配长度,只不过它的实现基于更为方便快捷的位运算,算法的实现需要用到<bitset>这个头文件,点击查看其功能用法。bitset类实现了通过类似数组下标的方式访问对象的每一个bit位,极大方便了对于该对象储存信息的提取。

算法大致思路:

1.根据模式串每位的字符建立字符表,如 a 对应的(bitset<5>)"00101",表示在模式串的1,3位上可以是字符a。

2.初始化储存结果的bitset对象 D 为"00000",表示没有一个字符成功匹配,(即没有一个字符既是完整模式串的前缀,又是当前目标串的后缀,通俗来讲就是,失配后起始位要老老实实挪到下一位,跳不成)。之后则不断从目标串中读入字符,并套用公式

                                                               D=(D<<1|1)&B[s[i]];

  表示将原结果左移一位,将[1]位置为1,表示对于新的一位匹配的期望,如果B[s[i]]第一位刚好也为1,则D串中即可更新。

  该&操作的意义是:(D<<1|1)试图将所有匹配位均向左增加一位,如果新加入的字符模式串中该位出现过,则匹配串升级成功。

3.时刻检查,如果D中n位为1,则证明出现一组匹配结果。

 

 

下面展示样题:

2016 大连ICPC B

数字串匹配,模式串(长度小于1000)中的每个位上,均有若干个可选项,给定一个较长的目标串(长度小于5*1e6),输出所有匹配结果。

 

#include<iostream>
#include<bitset>
#include<cstdio>
#include<cstring>

using namespace std;

bitset<1001> b[10],ans;
char s[5000005];
int n,len;

int main()
{
    cin>>n;
    char ch;
    for(int i=1; i<=n; i++)
    {
        int m;
        scanf("%d",&m);
        for(int j=0; j<m; j++)
        {
            int x;
            scanf("%d",&x);
            b[x][i]=1;
        }
    }
    scanf("%s",s+1);
    len=strlen(s+1);
    for(int i=1; i<=len; i++)
    {
        ans[1]=1;
        ans&=b[s[i]-'0'];
        if(ans[n]==1)
        {
            ch=s[i+1];
            s[i+1]=0;
            puts(s+i-n+1);
            s[i+1]=ch;
        }
        ans<<=1;
    }
    return 0;
}

 

压着超时的线通过,通过这道题发现直接用puts比一个一个putchar快不少。

全部评论

相关推荐

专心打鱼:互联网搬运工,贴子都要偷
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务