更高效的字符串匹配算法——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快不少。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Absoler

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值