• 3208. 【JSOI2013】编程作业


    Description&Data Constraint

    Will相信,很多同学都有过这样的经历:大牛已经写好了编程作业,而作为菜鸟的自己不会写怎么办呢?拿大牛的代码抄一下嘛!但是提交一模一样的作业是不是不太好?于是就改一改变量名什么的……但是其实这样的代码抄袭行为是可以被检测出来的。

    考虑到如下的两段代码,很容易发现他们其实是一样的。

    那么最开始给出的两段雷同代码就可以分别写成 AiBjCiDECjDiFGC 以及 AaBiCaDECiDaFGC。或者简单的说,我们认为这两段代码是一样的。

    现在请写一个程序,处理若干这样的代码雷同检测问题:给一个完整代码以及一个较短的代码片段,请求出,这个代码片段在完整代码中一共出现了多少次(代码片段出现的位置可以重叠)。

    为了简单起见,我们认为程序中只会至多出现 (a)~(z) 这26个变量,同时也至多只有 (A) ~ (Z) 这26个非变量符号。

    对于100%的数据满足 (Qle3)(|T|le100000)(|S|le1000000)

    Solution

    这题就是一个很明显的KMP,但是关键在于对小写字母的处理。

    其实可以发现,小写字母是什么并不重要,重要的是他们之间的位置。而大写字母要保持一致。

    因此可以对于每个小写字母,那个位置变成他与上一个同样的字母之间的距离,如果这个距离是一样的,那么就是可以匹配的。

    但是这并不是所有。

    举个例子,完整代码: (aababa),代码片段:(cdcd)

    根据上述的转换可以变成 (010222)(0022)。发现如果仅仅在这上面跑 KMP 是会遗漏答案的。

    因为对于位置 4 的 (a) 来说,在完整的代码中它距离上一个为 2,但是如果在完整片段中从第 3 个开始匹配,第 4 个 (a) 就变成了第一个,就可以与代码片段匹配。

    所以对于当前匹配的区间中,如果一个字母是第一次出现,那么它可以跟任何一个数字匹配,前提是在当前区间中它是第一个。

    Code

    #include<cstdio>
    #include<cstring>
    #define N 1000005
    using namespace std;
    int T,n,m,ans,match[N<<1],t[30],a[N],b[N],s[N<<1];
    char ch;
    void init1()
    {
    	memset(t,-1,sizeof(t));
    	while (!((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z'))) ch=getchar();
    	while (((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z')))
    	{
    		if (ch>='a'&&ch<='z')
    		{
    			t[ch-'a'+1]==-1?a[n]=0:a[n]=n-t[ch-'a'+1]; 
    			t[ch-'a'+1]=n;
    			++n;
    		}
    		else a[n++]=-(ch-'A'+1);
    		ch=getchar();
    	}
    }
    void init2()
    {
    	memset(t,-1,sizeof(t));
    	while (!((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z'))) ch=getchar();
    	while (((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z')))
    	{
    		if (ch>='a'&&ch<='z')
    		{
    			t[ch-'a'+1]==-1?b[m]=0:b[m]=m-t[ch-'a'+1]; 
    			t[ch-'a'+1]=m;
    			++m;
    		}
    		else b[m++]=-(ch-'A'+1);
    		ch=getchar();
    	}
    }
    bool check(int x,int y,int len)
    {
    	if (x<0||y<0) return x==y;
    	return x==y||(y==0&&x>len);
    }
    int main()
    {
    	scanf("%d",&T);
    	while (T--)
    	{
    		n=m=ans=0;
    		memset(match,0,sizeof(match));
    		memset(a,0,sizeof(a));
    		memset(b,0,sizeof(b));
    		init1();init2();
    		for (int i=0;i<m;++i)
    			s[i]=b[i];
    		s[m]=100;
    		for (int i=0;i<n;++i)
    			s[m+i+1]=a[i];
    		for (int i=1;i<=n+m;++i)
    		{
    			int j=match[i-1];
    			while (j>0&&!check(s[i],s[j],j)) j=match[j-1];
    			if (check(s[i],s[j],j)) ++j;
    			match[i]=j;
    		}
    		for (int i=m+1;i<=n+m;++i)
    			if (match[i]==m) ++ans;
    		printf("%d
    ",ans);
    	}
    	return 0;
    } 
    
  • 相关阅读:
    gojs常用API-画布定义
    页面开发的标准
    iis7.5做反向代理配置方法实例图文教程
    Tomcat实现反向代理
    nodejs的package.json依赖dependencies中 ^ 和 ~ 的区别
    dependencies与devDependencies的区别
    常见的cmd命令
    解决SecureCRT中文显示乱码
    ASP防XSS代码
    Android页面之间进行数据回传
  • 原文地址:https://www.cnblogs.com/Livingston/p/15367336.html
Copyright © 2020-2023  润新知