• [KMP]一本通(http://ybt.ssoier.cn:8088) 1698:字符串匹配


    字符串匹配

    【题目描述】

    对于一个字符集大小为C的字符串pp,可以将任意两个字符在p中的位置进行互换,例如p=12321,交换1212得到21312,交换14得到42324,交换可以进行任意次。若交换后p变成了字符串q,则成qp是匹配的。

    给定两个字符集大小为C的字符串st,求出s中有多少个连续子串与t匹配。

    【输入】

    第一行两个整数TC,分别表示数据组数和字符集大小,字符用1C的整数来表示。

    对于每组数据:第一行两个整数nm,分别表示st的长度。

    第二行n个正整数表示s

    第三行m个正整数表示t

    【输出】

    对于每组数据,输出包括两行:

    第一行一个正整数k,表示s中有k个连续子串与t匹配。

    第二行从小到大输出k个数,表示s中与t匹配的连续子串的首位下标(下标从1开始)。

    【输入样例】

    3 3

    6 3

    1 2 1 2 3 2

    3 1 3

    6 3

    1 2 1 2 1 2

    3 1 3

    6 3

    1 1 2 1 2 1

    3 1 3

    【输出样例】

    3

    1 2 4

    4

    1 2 3 4

    3

    2 3 4

    【数据规模及约定】

    对于10%的数据,满足n,m,C1000n,m,C1000

    对于另外20%的数据,满足n,m105C40n,m105C40

    对于另外30%的数据,满足n,m,C105n,m,C105

    对于100%的数据,满足1n,m,C106T=31n,m,C106T=3

    【分析】

    这其实就是一道KMP的题

    题目的难点在于如何交换字符

    我们可以开一个数组l[1~c]

    l[x]表示上一个x出现的位置

    a[i]表示字符s[i]离上一个相同字符出现的距离

    b[i]表示字符t[i]离上一个相同字符出现的距离

    然后就是KMP

    难点是如何判断s[i](t[i])的上一个相同字符是否在模式串之外

    这其实很简单

    直接判断上一个x出现的距离是否大于j不就行了

    于是开两个函数

    inline int jdg(int x,int l){return x<l?x:0;}//判断s[i](t[i])的上一个相同字符是否在匹配范围内
                                  //是就返回a[i](b[i]),否就返回0
    inline bool eq(int x,int y,int l){return jdg(x,l)==jdg(y,l);}//判断是否匹配

     经历千辛万苦

    【AC代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N (1000000+2)
    #define C (1000000+2)
    using namespace std;
    int p[N];
    int a[N],b[N];
    int l[C];//这里很重要,我把C开小了交了不知多少次都没有过
    int ans[N];
    template<typename T>inline void read(T& x){
    	char temp=getchar();bool u=0;
    	for(x=0;temp<'0'||temp>'9';u=temp=='-',temp=getchar());
    	for(;temp>='0'&&temp<='9';x=x*10+temp-'0',temp=getchar());
    	if(u)x=-x;
    	return ;
    }//快读
    inline int jdg(int x,int l){return x<l?x:0;}//判断s[i](t[i])是否在匹配范围内
                                  //是就返回a[i](b[i]),否就返回0
    inline bool eq(int x,int y,int l){return jdg(x,l)==jdg(y,l);}//判断是否匹配
    void work(){
    	register int i,j,x;
    	register int n,m;
    	read(n);
    	read(m);
    	memset(a,0,sizeof a);
    	memset(b,0,sizeof b);//有多组数据,一定要初始化
    	memset(p,0,sizeof p);//否则就会成为某dengzhaoxing之二
    	memset(l,0,sizeof l);
    	for(i=1;i<=n;i++){
    		read(x);
    		a[i]=i-l[x];//将a[i]赋值为字符x与上一x之间的距离
    		l[x]=i;
    	}
    	memset(l,0,sizeof l);//输入s和t之前都要初始化l
    	for(i=1;i<=m;i++){
    		read(x);
    		b[i]=i-l[x];//将b[i]赋值为字符x与上一x之间的距离
    		l[x]=i;
    	}
    	for(i=1,j=0;i<m;i++){
    		while(j&&!eq(b[i+1],b[j+1],j+1))
    			j=p[j];
    		if(eq(b[i+1],b[j+1],j+1))
    			j++;
    		p[i+1]=j;
    	}//KMP初始化p数组
    	for(x=i=j=0;i<n;i++){
    		while(j&&!eq(a[i+1],b[j+1],j+1))
    			j=p[j];
    		if(eq(a[i+1],b[j+1],j+1))
    			j++;
    		if(j==m){
    			ans[++x]=i+2-m;
    			j=p[j];
    		}
    	}//KMP匹配
    	printf("%d
    ",x);
    	for(i=1;i<=x;i++)
    		printf("%d ",ans[i]);//输出答案
    	putchar('
    ');
    	return ;
    }
    int main(){
    	register int t,c,i;
    	read(t);
    	read(c);
    	for(i=1;i<=t;i++)
    		work();
    	return 0;
    }
    

      

      

      

  • 相关阅读:
    jsp第三次作业
    C223生产版本,调用HTTP,实时传输生产版本
    在KO88结算成本之后,对特定的内部订单类型,替换默认的统驭科目
    SQL SERVER函数——表值函数的处理
    SQL SERVER行转列应用小结
    SQL SERVER里的赋值机制闲聊
    告别ADO.NET实现应用系统无缝切换的烦恼(总结篇)
    时间的起始
    用心若镜
    You get a dream...you gotta protect it.
  • 原文地址:https://www.cnblogs.com/TbIblog/p/11247312.html
Copyright © 2020-2023  润新知