多串匹配

摘要:
流量计多个字符串与DescriptionInput匹配。第一行是整数n,表示文本的长度。第二行是整数m,表示模式字符串的数量后跟m行。每行有m行的模式字符串输出。如果文本中出现第i个模式字符串,则第i行输出YES,否则输出NO数据范围为数据的30%,n˂=10^3,m˂=10^ 3;对于80%的数据,n˂=10^5,m˂=10^ 4;对于100%数据,n˂=10^5,m˂=10^ 5。SampleInput25saintzeussynthiathanehere3cynthiaerathenaSampleOutputYESNOYESSource后缀数组被给定一个长度为n的要匹配的固定字符串S,然后每次输入长度为m的模式字符串P。需要返回S中P的匹配或匹配失败。

meteor多串匹配

Description

Input

第一行为一个整数n,表示文本的长度 
第二行为一个长度为n的文本 
第三行为一个整数m,表示模式串个数 
下接m行,每行一个模式串 

Output

共m行,若第i个模式串在文本中出现过则第i行输出YES,否则输出NO 
数据范围 
对于30%的数据,n<=10^3,m<=10^3; 
对于80%的数据,n<=10^5,m<=10^4; 
对于100%的数据,n<=10^5,m<=10^5。 
输入文件大小不超过1M。 

Sample Input

25
saintzeuscynthiathenahere
3
cynthia
hera
athena

Sample Output

YES
NO
YES

Source

后缀数组

给定一个固定待匹配串S,长度为n,然后每次输入一个模式串P,长度为
m,要求返回P 在S 中的一个匹配或者返回匹配失败。所谓匹配指某个位置i
满足1≤i≤n-m+1 使得S[i..(i+m-1)]=P,也即Suffix(i)=mP。
我们知道,如果只有一个模式串,最好的算法就是KMP 算法,时间复杂
度为O(n+m),但是如果有多个模式串,我们就要考虑做适当的预处理使得对每
个模式串进行匹配所花的时间小一些。
最简单的预处理莫过于建立S 的后缀数组(先在S 的后面添加'$'),然后每
次寻找匹配转化为用二分查找法在SA 中找到和P 的公共前缀最长的一个后缀,
判断这个最长的公共前缀是否等于m。
这样,每次比较P 和一个后缀的复杂度为O(m),因为最坏情况下可能比较
了m 个字符。二分查找需要调用比较的次数为O(logn),因此总复杂度为
O(mlogn),于是每次匹配的复杂度从O(n+m)变为O(mlogn),可以说改进了不
少。
可是这样仍然不能令我们满足。前面提到LCP 可以增加后缀数组的威力,
我们来试试用在这个问题上。
我们分析原始的二分查找算法,大体有以下几步:

Step 1 令 left=1,right=n,max_match=0。
Step 2 令 mid=(left+right)/2(这里“/”表示取整除法)。
Step 3 顺次比较Suffix(SA[mid])和P 的对应字符,找到两者的最长公共
前缀r,并判断出它们的大小关系。若r>max_match则令max_match=r,ans=mid。
Step 4 若Suffix(SA[mid])<P 则令left=mid+1,若Suffix(SA[mid])>P 则令
right=mid-1,若Suffix(SA[mid])=P 则转至Step 6。
Step 5 若 left<right 则转至Step 2,否则至Step 6。
Step 6 若 max_match=m则输出ans,否则输出“无匹配”。
注意力很快集中在Step 3,如果能够避免每次都从头开始比较Suffix(SA[mid])
和P 的对应字符,也许复杂度就可以进一步降低。
类似于前面求height 数组,我们考虑利用以前求得的最长公共前缀作为比
较的“基础”,避免冗余的字符比较。

在比较Suffix(SA[mid])和P 之前,我们先用常数时间计算LCP(mid,ans),
然后比较LCP(mid,ans)和max_match:
情况一:LCP(mid,ans)<max_match,则说明Suffix(SA[mid])和P 的最长公
共前缀就是LCP(mid,ans),即直接可以确定Step 3 中的r=LCP(mid,ans),所以
可以直接比较两者的第r+1 个字符(结果一定不会是相等)就可以确定
Suffix(SA[mid])和P 的大小。这种情况下,字符比较次数为1 次。
情况二:LCP(mid,ans)≥max_match,则说明Suffix(SA[mid])和Suffix(SA[ans])
的前max_match个字符一定是相同的,于是Suffix(SA[mid])和P 的前max_match
个字符也是相同的,于是比较两者的对应字符可以从第max_match+1 个开始,
最后求出的r 一定大于等于原先的max_match,字符比较的次数为rmax_
match+1,不难看出Step 3 执行过后max_match将等于r。
设每次Step 3 执行之后max_match 值增加的量为Δmax。在情况一中,
Δmax=0,字符比较次数为1=Δmax+1;在情况二中,Δmax=r-max_match,字符
比较次数为r-max_match+1,也是Δmax+1。综上所述,每次Step 3 进行字符比
较的次数为Δmax+1。

总共的字符比较次数为所有的Δmax累加起来再加上Step 3执行的次数。所
有Δmax累加的结果显然就是最后的max_match值,不会超过len(P)=m,而Step
3 执行的次数为O(logn),因此总共的字符比较次数为O(m+logn)。而整个算法
的复杂度显然和字符比较次数同阶,为O(m+logn)。
至此,问题得到圆满解决,通过O(nlogn)的时间进行预处理(构造后缀数
组、名词数组,计算height 数组,RMQ 预处理),之后就可以在O(m+logn)的
时间内对一个长度为m 的模式串P 进行模式匹配,这仅仅是在读入P 的复杂度
上附加了logn的复杂度,是非常优秀的。

code:

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#define maxn 100010
using namespace std;
char ch,s[maxn],ss[maxn];
int n,m,l,r,mid,len,tot,max_match,t_len,ans;
int SA[maxn],rank[maxn<<1],tmp[maxn],sum[maxn],height[maxn],tree[maxn<<2];
bool bo,stop;
void read(int &x){
    for (ch=getchar();!isdigit(ch);ch=getchar());
    for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());
}
void get_SA(){
    int *x=t1,*y=t2;
    for (int i=1;i<=n;i++) sum[x[i]=s[i]]++;
    for (int i=1;i<=255;i++) sum[i]+=sum[i-1];
    for (int i=1;i<=n;i++) SA[sum[x[i]]--]=i;
    tot=0;
    for (int len=1;tot<n;len<<=1,m=tot){
        tot=0;
        for (int i=n-len+1;i<=n;i++) y[++tot]=i;
        for (int i=1;i<=n;i++) if (SA[i]>len) y[++tot]=SA[i]-len;
        memset(sum,0,sizeof(sum));
        for (int i=1;i<=n;i++) sum[x[y[i]]]++;
        for (int i=1;i<=m;i++) sum[i]+=sum[i-1];
        for (int i=n;i>=1;i--) SA[sum[x[y[i]]]--]=y[i];
        swap(x,y);
        x[SA[1]]=tot=1;
        for (int i=2;i<=n;i++){
            if (y[SA[i]]!=y[SA[i-1]]||y[SA[i]+len]!=y[SA[i-1]+len]) tot++;
            x[SA[i]]=tot;    
        }
    }
    for (int i=1;i<=n;i++) rank[i]=x[i];
}
void get_height(){
    for (int i=1,j=0;i<=n;i++){
        if (rank[i]==1) continue;
        while (s[i+j]==s[SA[rank[i]-1]+j]) j++;
        height[rank[i]]=j;
        if (j>0) j--;    
    }    
}
inline void build(int k,int l,int r){
    if (l==r){
        tree[k]=height[l];
        return;
    }    
    int m=(l+r)>>1;
    build(k<<1,l,m),build((k<<1)+1,m+1,r);
    tree[k]=min(tree[k<<1],tree[(k<<1)+1]);
}
inline int answer(int k,int l,int r,int x,int y){
    if (l==x&&r==y) return tree[k];
    int m=(l+r)>>1;
    if (y<=m) return answer(k<<1,l,m,x,y);
    else if (x<=m) return min(answer(k<<1,l,m,x,m),answer((k<<1)+1,m+1,r,m+1,y));    
    else return answer((k<<1)+1,m+1,r,x,y);
}
inline int LCP(int i,int j){
    if (i==j) return n-SA[i]+1;
    if (i>j) swap(i,j);
    return answer(1,1,n,i+1,j);    
}
int main(){
    read(n);
    scanf("%s",s+1);
    get_SA(),get_height(),build(1,1,n);
    read(m);
    for (int i=1;i<=m;i++){
        scanf("%s",ss+1);
        len=strlen(ss+1);
        l=1,r=n,max_match=ans=0;
        while (l<=r){
            mid=(l+r)>>1,bo=0,stop=0;
            if (!ans){
                int i;
                for (i=SA[mid];i<=n&&ss[max_match+1]==s[i];i++,max_match++,ans=mid);
                if (s[i]<ss[max_match+1]) bo=1;
                
            }
            else{
                t_len=LCP(ans,mid);
                if (t_len<max_match){
                    if (s[t_len+SA[mid]]<ss[t_len+1]) bo=1;
                }
                else{
                    int i;
                    for (i=SA[mid]+max_match;i<=n&&ss[max_match+1]==s[i];i++,max_match++,ans=mid);
                    if (s[i]<ss[max_match+1]) bo=1;
                }
            }
            if (max_match>=len) break;
            if (bo) l=mid+1;
            else r=mid-1;
        }
        if (max_match>=len) puts("YES");
        else puts("NO");
    }
    return 0;    
}

免责声明:文章转载自《多串匹配》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇字典、列表之间相互嵌套makefile中的自动化变量 【转】下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

PIL中的Image和numpy中的数组array相互转换

1. PIL image转换成array img = np.asarray(image) 需要注意的是,如果出现read-only错误,并不是转换的错误,一般是你读取的图片的时候,默认选择的是"r","rb"模式有关。 修正的办法: 手动修改图片的读取状态 img.flags.writeable = True # 将数组改为读写模式 2....

解决PHP json_encode() 编码字符中包含<>时,转化为\u003E\u003C

一、PHP json_encode里面经常用到的 JSON_UNESCAPED_UNICODE和JSON_UNESCAPED_SLASHES php格式化json的函数 json_encode($value,$options) 其中有2个比较常用到的参数 JSON_UNESCAPED_UNICODE(中文不转为unicode ,对应的数字 256) J...

正则表达式(一)

一、简介 正则表达式这个名词,相信很多人都听说过,这个名词最早起源于1956 年, 一位叫 Stephen Kleene 的美国数学家在 McCulloch 和 Pitts 早期工作的基础上,发表了一篇标题为“神经网事件的表示法”的论文,引入了正则表达式的概念。正则表达式就是用来描述他称为“正则集的代数”的表达式,因此采用“正则表达式”这个术语。 随后,发...

使用java8的stream对数组进行求和

1、对BigDecimal类型的值求和。 List<Map<String,Object>> list = new ArrayList<>(); Map<String,Object> stu1 = new HashMap<String, Object>(); stu1.put("name", "张三...

Java-数据类型(八种基本数据类型)

1、整数类型:byte,short,int,longbyte:一般跟文件操作有关,比如上传、下载。长度8位,-128-127 byte numbyte1=133; //报错:cannot convert from int to byte //不能从int类型转换为byte类型 //整数常数看作int类型,但是如果取值范围在-128-127之间的话,自动把i...

字符串匹配算法

一、简介 文本信息可以说是迄今为止最主要的一种信息交换手段,而作为文本处理中的一个重要领域——字符串匹配,就是我们今天要说的话题。(原文还特意提及文本数据数量每18个月翻一番,以此论证算法必须要是高效的。不过我注意到摩尔定律也是18个月翻番,这正说明数据的增长是紧紧跟随处理速度的,因此越是使用高效的算法,将来待处理的数据就会越多。这也提示屏幕前的各位,代码...