hdu5716带可选字符的多字符串匹配

博客主要讲解了一种名为柔性字符串匹配的算法,涉及HDU5716题目。通过分析,博主提到了可以使用shift-and算法,并讨论了如何使用bitset进行优化,提出两种不同的实现方式,一种常数较大但时间复杂度为O(k*n*m/64),另一种常数较小的时间复杂度为O(n*m/64)。
摘要由CSDN通过智能技术生成

链接:http://acm.hdu.edu.cn/showproblem.php?pid=5716

题意:中文题。

分析:之前补过一次百度之星的复赛,补到这题的时候没去找人问怎么做。后来做了一个XG出的多校hdu5745这个题知道了这种优化,但是当时没有联想到怎么做。直到这次打大连现场赛的时候看到B题。???这TM和百度之星那题不是一模一样?然后就后悔当时没有补完这个题。回到正题,这个题的这种思路是叫柔性字符串匹配:shift-and算法。对于这题而言,我们如果有过一点推导会发现,我们可以画出一个m*n的矩阵,第i行第j列用0/1表示子串的第i个字符集是否能匹配母串中的第j个字符。如果对于母串中的第j个字符为起点能够匹配完全子串那么在矩阵中就应该是一条"\"的斜率为1的全是1的线从第1行串到第m行。O(n*m)的做法太暴力了,看到只有0/1我们想到用bitset优化,但是斜线怎么优化呢?其实简单的移1位就能将斜线变为直线然后我们只要用&就能判断某个位置能否匹配完全啦。详细实现的话这里普遍会有两种写法,第一种是带较大常数的O(k*n*m/64),k是字符集的大小,假设字符集元素个数为k个,那么我们开k+2个长度为n(母串长度)的bitset,用k个保存每个字符在母串的哪些位置出现。设匹配到当前位置母串中还剩哪些位置是匹配成功的保存在ans内,然后我们将当前位置的这个字符集(比如样例中的3 abc),我们先将母串中a,b,c出现的位置或起来存到now内,那么我们就得到了这个位置能匹配的所有位置,然后我们将答案ans=(ans<<1)&now就能更新了,最后ans中剩的1的位置就是匹配成功的结尾处。第二种是常数小的O(n*m/64),我们先预处理出一个k*m的bitset的矩阵w,第i行第j列表示第i个字符是否在第j个字符集里。我们用两个长度为m的bitset的ans和now来滚动维护答案,ans[i]==1表示维护到当前位置长度为i是可行的。那么每次我们维护now=(ans<<1)&w[s[i]][j],ans=now就行了。还不懂的话看代码吧,在评论中问也行。

代码:

#include<map>
#include<set>
#include<stack>
#include<cmath>
#include<queue>
#include<bitset>
#include<math.h>
#include<vector>
#include<string>
#include<stdio.h>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=2000010;
const int M=510;
const int mod=1000000007;
const int MOD1=1000000007;
const int MOD2=1000000009;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=1000000007;
const int INF=2000000010;
const ll MAX=1ll<<55;
const double eps=1e-5;
const double inf=~0u>>1;
const double pi=acos(-1.0);
typedef double db;
typedef unsigned int uint;
typedef unsigned long long ull;
char s[N],t[65];
bitset<M>w[300],P[2];
int main()
{
    int i,j,n,bo,len,now;
    while (gets(s+1)) {
        scanf("%d", &n);
        for (i=0;i<256;i++) w[i].reset();
        for (i=1;i<=n;i++) {
            scanf("%d%s", &len, t+1);
            for (j=1;j<=len;j++) w[t[j]][i]=1;
        }
        bo=now=0;
        P[now].reset();P[now][0]=1;
        for (i=1;s[i];i++) {
            P[!now]=(P[now]<<1)&w[s[i]];
            now^=1;P[now][0]=1;
            if (P[now][n]) {
                bo=1;printf("%d\n", i-n+1);
            }
        }
        if (!bo) printf("NULL\n");
        getchar();
    }
    return 0;
}


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值