HDU 1754(zkw线段树-区间最值)

I Hate It


Problem Description
很多学校流行一种比较的习惯。老师们很喜欢询问,从某某到某某当中,分数最高的是多少。
这让很多学生很反感。

不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问。当然,老师有时候需要更新某位同学的成绩。

 


Input
本题目包含多组测试,请处理到文件结束。
在每个测试的第一行,有两个正整数 N 和 M ( 0<N<=200000,0<M<5000 ),分别代表学生的数目和操作的数目。
学生ID编号分别从1编到N。
第二行包含N个整数,代表这N个学生的初始成绩,其中第i个数代表ID为i的学生的成绩。
接下来有M行。每一行有一个字符 C (只取'Q'或'U') ,和两个正整数A,B。
当C为'Q'的时候,表示这是一条询问操作,它询问ID从A到B(包括A,B)的学生当中,成绩最高的是多少。
当C为'U'的时候,表示这是一条更新操作,要求把ID为A的学生的成绩更改为B。
 


Output
对于每一次询问操作,在一行里面输出最高成绩。
 


Sample Input
5 6 1 2 3 4 5 Q 1 5 U 3 6 Q 3 4 Q 4 5 U 2 9 Q 1 5
 


Sample Output
5 6 5 9
Hint
Huge input,the C function scanf() will work better than cin
 

zkw线段树中,解决区间最值问题的方法——差分

让我们以从点到根节点的路径上数的和为这个点的区间对应最大值。

很显然除根节点外,其它值皆为负数,且它必有一个儿子结点的值为0(叶结点除外)。


则当我们修改一个点的值时,先计算它原来的值,看看是否增加。

维护它与父节点的关系(它的修改值应减去路径上的值为它的当前值,而非直接修改(在原数上的增减除外)

求区间最值方法:

首先从子节点递归,存下它左右子节点所能递归到的最大值.

PS:1.记得每次向上加上路径上的值,因为它若取邻结点,则它们向上从父节点开始的部分完全相等。

      2.一开始那个点不能考虑(赋初值为-INF)

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<iostream>
#include<cctype>
#include<functional>
#include<algorithm>
using namespace std;
#define MAXN (200000+10)
#define MAXm (5000+10)
#define INF (0xfffffff)
int n,m,M,t[MAXN*5];
char c[100];
int Query(int p1,int p2)
{
	p1--;p2++;p1+=M;p2+=M;
	int LANS=-INF,RANS=-INF;
	while (p1^p2^1)
	{
		LANS+=t[p1];RANS+=t[p2];
		if (~p1&1) LANS=max(LANS,t[p1+1]);
		if (p2&1)  RANS=max(RANS,t[p2-1]);
		p1>>=1;p2>>=1;
	}
	LANS+=t[p1];RANS+=t[p2];
	int ANS=max(LANS,RANS);
	for (p1>>=1;p1;p1>>=1) ANS+=t[p1];
	return ANS;
}
int main()
{
//	freopen("I hate it.in","r",stdin);
	while (scanf("%d%d",&n,&m)!=EOF)
	{
		memset(t,0,sizeof(t));
		M=1;while (M-2<n) M<<=1;
		for (int i=M+1;i<=M+n;i++) scanf("%d",&t[i]);
		for (int i=M;i>=1;i--)
		{
			t[i]=max(t[i<<1],t[(i<<1)^1]);
			t[i<<1]-=t[i];t[(i<<1)^1]-=t[i];
		}

		for (int k=1;k<=m;k++)
		{
			int p1,p2;
			scanf("%s%d%d",c,&p1,&p2);
			if (c[0]=='Q')
			{
			/*	p1--;p2++;p1+=M;p2+=M;
				int LANS=-INF,RANS=-INF;
				while (p1^p2^1)
				{
					LANS+=t[p1];RANS+=t[p2];
					if (~p1&1) LANS=max(LANS,t[p1+1]);
					if (p2&1)  RANS=max(RANS,t[p2-1]);
					p1>>=1;p2>>=1;
				}
				LANS+=t[p1];RANS+=t[p2];
				int ANS=max(LANS,RANS);
				for (p1>>=1;p1;p1>>=1) ANS+=t[p1];
			*/	cout<<Query(p1,p2)<<endl;
			}
			else
			{
				p1+=M;
				t[p1]+=p2-Query(p1-M,p1-M);
				for (p1>>=1;p1;p1>>=1)
				{
					int A=max(t[p1<<1],t[(p1<<1)^1]);
					t[p1<<1]-=A;t[(p1<<1)^1]-=A;t[p1]+=A;
				}
			}

		}
	}
	return 0;
}


Vijos 1490(判断整除的方法)

89/283(31%)   首页 站务 公告 | 题库/分类/原题 记录 比赛 团队 讨论 | U-S 搜索 换肤 正體 | 登出  
 
   
  公告 News >>   New! 关于noip2012模拟赛的公告 (2012-10-28
15:35:15)
   New! 关于Vijos10月份月赛的公告 (2012-10-26
14:44:09)
   关于第一期内测结束的公告 (2012-9-23 22:43:11)   关于Vijos9月份月赛的公告 (2012-9-20
17:31:48)
   Vijos月赛正式开通 (2012-9-16 11:35:36)
 
     

   
 
From dmh123x

小菜的数码验证 

 
     
   
  描述 Description  
  小菜最近在学数的数字数字特征,因此他打算编程来研究一下这个问题. 

他的问题很简单,输入一个数N(1000<N<10^31),判断是否能整除2, 3,4,8分别整除,同时判断它是否有可能(注意只是有可能) 

是完全平方数,依次输出1和0来表示能或不能. 
     
     
  输入格式 Input Format  
  一个自然数N(1000<N<10^31) 
     
     
  输出格式 Output Format  
  5个数,1或者0分别表示能和不能
     
     
  样例输入 Sample Input  
 
     
     
  样例输出 Sample Output  
 
     
     
  时间限制 Time Limitation  
  各个测试点1s 
     
   
 
Flag
Accepted
题号
P1490
数论 / 数值
通过
639人
提交
1270次
通过率
50%
难度
1
 
     
     
 
提交 讨论 题解 状态
 
     
     
 

 
     


 
 

这题坑爹的地方在于:可能是完全平方数就是一定满足……ToT

好了,具体参见目录,此题没必要高精。(尽管 10^31 不是 2^31……)

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
using namespace std;
char s[5000];
int find(int k)
{
	if (strlen(s)>=k) return s[strlen(s)-1-k+1]-48;
	return 0;
}
int main()
{
	scanf("%s",s);
	if (find(1)%2) cout<<"0"<<endl; else cout<<"1"<<endl;
	int tot=0; for (int i=1;i<=strlen(s);i++) tot+=find(i);
	if (tot%3) cout<<"0"<<endl; else cout<<"1"<<endl;
	if ((10*find(2)+find(1))%4) cout<<"0"<<endl; else cout<<"1"<<endl;
	if ((100*find(3)+10*find(2)+find(1))%8) cout<<"0"<<endl; else cout<<"1"<<endl;
	cout<<1<<endl;
	return 0;
}

附录:判定整除的一般性方法:

(1)1与0的特性: 

1是任何整数的约数,即对于任何整数a,总有1|a. 

0是任何非零整数的倍数,a≠0,a为整数,则a|0. 

(2)若一个整数的末位是0、2、4、6或8,则这个数能被2整除。 

(3)若一个整数的数字和能被3整除,则这个整数能被3整除。 

(4) 若一个整数的末尾两位数能被4整除,则这个数能被4整除。 

(5)若一个整数的末位是0或5,则这个数能被5整除。 

(6)若一个整数能被2和3整除,则这个数能被6整除。 

(7)若一个整数的个位数字截去,再从余下的数中,减去个位数的2倍,如果差是7的倍数,则原数能被7整除。如果差太大或心算不易看出是否7的倍 数,就需要继续上述「截尾、倍大、相减、验差」的过程,直到能清楚判断为止。例如,判断133是否7的倍数的过程如下:13-3×2=7,所以133是7 的倍数;又例如判断6139是否7的倍数的过程如下:613-9×2=595 , 59-5×2=49,所以6139是7的倍数,余类推。 

(8)若一个整数的未尾三位数能被8整除,则这个数能被8整除。 

(9)若一个整数的数字和能被9整除,则这个整数能被9整除。 

(10)若一个整数的末位是0,则这个数能被10整除。 

(11)若一个整数的奇位数字之和与偶位数字之和的差能被11整除,则这个数能被11整除。11的倍数检验法也可用上述检查7的「割尾法」处理!过程唯一不同的是:倍数不是2而是1! 

(12)若一个整数能被3和4整除,则这个数能被12整除。 

(13)若一个整数的个位数字截去,再从余下的数中,加上个位数的4倍,如果差是13的倍数,则原数能被13整除。如果差太大或心算不易看出是否13的倍数,就需要继续上述「截尾、倍大、相加、验差」的过程,直到能清楚判断为止。 

(14)若一个整数的个位数字截去,再从余下的数中,减去个位数的5倍,如果差是17的倍数,则原数能被17整除。如果差太大或心算不易看出是否17的倍数,就需要继续上述「截尾、倍大、相减、验差」的过程,直到能清楚判断为止。 

(15)若一个整数的个位数字截去,再从余下的数中,加上个位数的2倍,如果差是19的倍数,则原数能被19整除。如果差太大或心算不易看出是否19的倍数,就需要继续上述「截尾、倍大、相加、验差」的过程,直到能清楚判断为止。 

(16)若一个整数的末三位与3倍的前面的隔出数的差能被17整除,则这个数能被17整除。 

(17)若一个整数的末三位与7倍的前面的隔出数的差能被19整除,则这个数能被19整除。 

(18)若一个整数的末四位与前面5倍的隔出数的差能被23(或29)整除,则这个数能被23整除 

HDU 1166(zkw线段树-单点修改)

敌兵布阵

Problem Description
C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了。A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任务就是要监视这些工兵营地的活动情况。由于采取了某种先进的监测手段,所以每个工兵营地的人数C国都掌握的一清二楚,每个工兵营地的人数都有可能发生变动,可能增加或减少若干人手,但这些都逃不过C国的监视。
中央情报局要研究敌人究竟演习什么战术,所以Tidy要随时向Derek汇报某一段连续的工兵营地一共有多少人,例如Derek问:“Tidy,马上汇报第3个营地到第10个营地共有多少人!”Tidy就要马上开始计算这一段的总人数并汇报。但敌兵营地的人数经常变动,而Derek每次询问的段都不一样,所以Tidy不得不每次都一个一个营地的去数,很快就精疲力尽了,Derek对Tidy的计算速度越来越不满:"你个死肥仔,算得这么慢,我炒你鱿鱼!”Tidy想:“你自己来算算看,这可真是一项累人的工作!我恨不得你炒我鱿鱼呢!”无奈之下,Tidy只好打电话向计算机专家Windbreaker求救,Windbreaker说:“死肥仔,叫你平时做多点acm题和看多点算法书,现在尝到苦果了吧!”Tidy说:"我知错了。。。"但Windbreaker已经挂掉电话了。Tidy很苦恼,这么算他真的会崩溃的,聪明的读者,你能写个程序帮他完成这项工作吗?不过如果你的程序效率不够高的话,Tidy还是会受到Derek的责骂的.
 


Input
第一行一个整数T,表示有T组数据。
每组数据第一行一个正整数N(N<=50000),表示敌人有N个工兵营地,接下来有N个正整数,第i个正整数ai代表第i个工兵营地里开始时有ai个人(1<=ai<=50)。
接下来每行有一条命令,命令有4种形式:
(1) Add i j,i和j为正整数,表示第i个营地增加j个人(j不超过30)
(2)Sub i j ,i和j为正整数,表示第i个营地减少j个人(j不超过30);
(3)Query i j ,i和j为正整数,i<=j,表示询问第i到第j个营地的总人数;
(4)End 表示结束,这条命令在每组数据最后出现;
每组数据最多有40000条命令
 


Output
对第i组数据,首先输出“Case i:”和回车,
对于每个Query询问,输出一个整数并回车,表示询问的段中的总人数,这个数保持在int以内。
 


Sample Input
1 10 1 2 3 4 5 6 7 8 9 10 Query 1 3 Add 3 6 Query 2 7 Sub 10 2 Add 6 3 Query 3 10 End
 


Sample Output
Case 1: 6 33 59
 

这题是zkw线段树中单点修改与区间查询的一般方法:

M的计算 显然最后一行是:

+M:  0 1 2 3....M-1

但是我们求和值是不能求(0,M-1),(zkw线段树不支持首尾查询)

我们查询的应该是开区间(a,b) (这样的目的是保证不出现当前无法判定是否取-

原理:在左边时左边不取,而若右边不为t(s^t≠1),则必取.

    在右边时右边不取,而若左边不为s(s^t≠1),则必取.

建树时要保证n<=M-2

M=1;while (M-2<n) M<<=1;

从C++的角度:

由于char s[]为数组,无法直接比较

需要用 strcmp(string a,string a)==0 判定

它是字符串的cmp 返回字典序比较情况

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<iostream>
using namespace std;
#define MAXN (50000+10)
#define MAXAi (50+10)
int n,m,k,M,tt,t[MAXN*5];
char s[50];
int main()
{
//	freopen("Hdu1166.in","r",stdin);
	scanf("%d",&tt);
	for (int k=1;k<=tt;k++)
	{
		printf("Case %d:n",k);
		scanf("%d",&n);
		memset(t,0,sizeof(t));
		M=1;while (M-2<n) M<<=1;
		for (int i=M+1;i<=M+n;i++) scanf("%d",&t[i]);
		for (int i=M;i>=1;i--) t[i]=t[i<<1]+t[(i<<1)^1];


		while (scanf("%s",s)!=EOF)
		{
			if (!strcmp(s,"End")) break;
			int p1,p2;
			scanf("%d%d",&p1,&p2);
			if (s[0]=='Q')
			{
				int ans=0;
				p1--;p2++;p1+=M;p2+=M;
				while (p1^p2^1>0)
				{
					if (~p1&1) ans+=t[p1+1];
					if (p2&1) ans+=t[p2-1];
					p1>>=1;p2>>=1;
				}
				cout<<ans<<endl;
				continue;
			}
			else
			{
				p1+=M;
				if (s[0]=='A') t[p1]+=p2; else t[p1]-=p2;
				for (p1/=2;p1;p1/=2)
				{
					t[p1]=t[p1<<1]+t[(p1<<1)^1];
				}

			}


		}






	}


	return 0;
}


POJ 2190(模拟策略)

Language:
ISBN
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 13962   Accepted: 4898

Description

ISBN码满足:(10*A1+9*A2+..+1*A10) mod 11=0
A10是效验码,若它为10,则以X代替,其它位都为0..9的数字
 现在给你一个效验码,但是其中一位看不清,请求出这一位。

Input

1行ISBN码,‘?'表示看不清

Output

输出那一位,无解输-1.

Sample Input

15688?111X

Sample Output

1

Source

这题就是模拟,模拟最重要的是细节,本题值得注意的细节:

1.某1位为0的情况.

2.其它位的乘积和是11的倍数.

3.某1位为X的情况.

4.无解.

5.其它位的乘积和包含'X'.

Program ISBN;
const
   n=10;
   F=11;
var
   s:string;
   i,p,ans:longint;
begin
   ans:=0;
   readln(s);
   if s[n]='X' then s[n]:=char(58);

   p:=pos('?',s);

   for i:=1 to n do
      if i<>p then inc(ans,(ord(s[i])-48)*(10-i+1));
   // ans +p*? mod 11 =0
   ans:=ans mod F;
   ans:=(F-ans) mod F;

   for i:=0 to n do
   begin
      if ((11-p)*i) mod F=ans then
      begin
         if (p=n) and (i=10) then begin writeln('X'); halt; end;
         if (i<10) then begin writeln(i);   halt; end;
      end;
   end;

   writeln('-1');

end.



POJ 1226(最长公共子串含逆序)

Language:
Substrings
Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 9639   Accepted: 3319

Description

请找出一些串的最长‘正/逆‘子串,使它为所有的串的子串(即使是逆序也认为包含).

Input

第一行为数据数t,(1 <= t <= 10),
对每组数据而言:第一行为字符串个数 n (1 <= n <= 100),接下来n行为字符串(长度不超过100) .

Output

每行一个数,表示最长'正/逆‘子串的长度。

Sample Input

2
3
ABCD
BCDFF
BRCD
2
rose
orchid

Sample Output

2
2 

Source

还是KMP,先在第一个串中枚举串,之后考察它是否是其它串的'正/逆'子串。


Program P1226;
const
   maxn=100;
   maxt=10;
var
   tt,n,m,i,j,k,ans:longint;
   flag:boolean;
   a:array[1..maxn] of string;
   p:string;
   next:array[1..maxn] of longint;

function kmp(a,b:string):boolean;
var
   i,j,n,m:longint;
begin
   i:=1;j:=0;next[1]:=0;
   n:=length(a); m:=length(b);
   while (i<m) do
   begin
      if (j=0) or (b[i]=b[j]) then
      begin
         inc(i);inc(j);
         if (b[i]<>b[j]) then next[i]:=j else next[i]:=next[j];
      end else j:=next[j];
   end;

   i:=0;j:=0;
   while (i<=n) and (j<=m) do
   begin
      if (j=0) or (a[i]=b[j]) then
      begin
         inc(i);inc(j);
      end else j:=next[j];
   end;
   if (j>m) then exit(true);
   exit(false);
end;
function ob_s(a:string):string;
var
   i,j,n:longint;
begin

   ob_s:=''; n:=length(a);
   for i:=n downto 1 do ob_s:=ob_s+a[i];
end;
function compare(a,b:string):boolean;
var
   n,m:longint;
begin
   n:=length(a);m:=length(b);
   if (n<>m) then exit(n<m);
   for i:=1 to n do
      if a[i]<>b[i] then exit(a[i]<b[i]);
   exit(false);
end;


begin
   readln(tt);
   while (tt>0) do
   begin
      ans:=0;
      readln(n);
      for i:=1 to n do readln(a[i]);

      for i:=1 to length(a[1]) do
         for j:=i to length(a[1]) do
         begin
            p:=copy(a[1],i,j-i+1);
            flag:=true;
            for k:=2 to n do
            begin
               if not((kmp(a[k],p) or kmp(a[k],ob_s(p)))) then
               begin
                  flag:=false; break;
               end;
            end;
            if flag and (length(p)>ans) then ans:=length(p);


         end;

      writeln(ans);
      dec(tt);
   end;
end.


POJ 2185(最大平铺矩阵)

Language:
Milking Grid
Time Limit: 3000MS   Memory Limit: 65536K
Total Submissions: 4346   Accepted: 1780

Description

给定R(1 <= R <= 10,000) *C (1 <= C <= 75) 的矩阵,求它的最大平铺矩阵,不够的地方可部分平铺,但不可重叠。

Input

第一行:R和C.
第2-R+1行每行C个大写字母,表示矩阵.

Output

最大的平铺矩阵面积

Sample Input

2 5
ABABA
ABABA

Sample Output

2

Hint

The entire milking grid can be constructed from repetitions of the pattern 'AB'.

Source

显然这个矩阵必然从左上角开始

由于C比较少,先找出每列最大的平铺线段(行行不影响)

再考虑每行共有且最小的重复部分(可以证明增加重复部分对行的大小无影响)

在考虑行R≤10000,必须用Kmp,不凡假设句末有若干'????'

则对于字符串的P

AEICCCAEICCCAEI C  C  ?   ?   ?  ...

000000123456789 10 11 12  13  14 ...

显然?后的P递增+1,又因答案为Max(i-p[i])(i≥n)

所以(n-p[n])=Max(i-p[i])


Program grid;
const
   maxn=10000;
   maxm=75;
var
   n,m,i,j,k:longint;
   a:array[1..maxn] of string;
   f:array[1..maxm] of longint;
   flag:boolean;
   p:array[1..maxn] of longint;
begin
   fillchar(f,sizeof(f),0);
   readln(n,m);
   for i:=1 to n do
   begin
      readln(a[i]);
      for j:=1 to m do
      begin
         flag:=true;
         for k:=j+1 to m do
            if a[i][k]<>a[i][k-j] then begin flag:=false; break; end;
            if flag then inc(f[j]);
      end;
   end;
   for i:=1 to m do if f[i]=n then break;
   m:=i;

   for i:=1 to n do delete(a[i],m+1,maxlongint);

   j:=0;p[1]:=0;
   for i:=2 to n do
   begin
      while (j>0) and (a[i]<>a[j+1]) do j:=p[j];
      if (a[i]=a[j+1]) then inc(j);
      p[i]:=j;
   end;

   n:=n-p[n];
   writeln(m*n);





end.







POJ 3461(模式匹配数&覆盖函数)

Language:
Oulipo
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 14051   Accepted: 5667

Description

给出两个字符串W和T,求T中有几个W子串。

Input

第一行为数据数.

每组数据有两行W和T,表示模式串和原始串.

Output

对每组数据,每行一个数,表示匹配数.

Sample Input

3
BAPC
BAPC
AZA
AZAZAZA
VERDI
AVERDXIVYERDIAN

Sample Output

1
3
0

Source


这题用到了KMP中的覆盖函数——P

P和Next的区别是P是指包括当前点的最长覆盖长度,Next是指匹配到i,若不满足条件,将其挪到Next[i],(P[i]<>P[next[i]],P表模式串)

证明:



之后进行查找,若查到(j=m),则j=P[j](为了下一次查找)


Program Poj3461;
const
   maxm=10000;
   maxn=1000000;
var
   tt,n,m,i,j:longint;
   a,b:ansistring;
   P:array[1..maxn] of longint;
function kmp:longint;
var
   n,m,i,j:longint;
begin
   kmp:=0;

   n:=length(a);m:=length(b);
   P[1]:=0;j:=0;
   for i:=2 to m do
   begin
      while (j>0) and (b[j+1]<>b[i]) do j:=p[j];
      if (b[j+1]=b[i]) then inc(j);
      p[i]:=j;
   end;

   j:=0;
   for i:=1 to n do
   begin
      while (j>0) and (b[j+1]<>a[i]) do j:=p[j];
      if (b[j+1]=a[i]) then inc(j);
      if j=m then
      begin
         inc(kmp);
         j:=p[j];
      end;

   end;


end;
begin
   readln(tt);
   while (tt>0) do
   begin
     readln(b);
     readln(a);

     writeln(kmp);

     dec(tt);
   end;

end.



POJ 1961(KMP前缀最长重复子串)

Language:
Period
Time Limit: 3000MS   Memory Limit: 30000K
Total Submissions: 10179   Accepted: 4662

Description

求一个字符串中,所有循环节大于2的子串。

Input

有若干组数据,
每组数据第一行为字符串长度,第二行为字符串
以0结束。

Output

对于每组数据,输出'Test case #i",i从0开始,之后每行输出两个数,分别表示前缀长度和循环节数(>1).
每组数据后输出一个空行。

Sample Input

3
aaa
12
aabaabaabaab
0

Sample Output

Test case #1
2 2
3 3

Test case #2
2 2
6 2
9 3
12 4

Source

这题就是KMP问题。

还是Next,

先做不判重复优化的处理

观察发现若前面有循环节,则有i->i-p->i-2p...->i-kp=1

则i-next[i]表示循环结长度p,显然有p|(i-1),(i-1) div p>1.

如下:



Program Poj1961;
const
   maxn=10000000;
var
   i,j,tt,n,duan_luo:longint;
   next:array[1..maxn] of longint;
   a:ansistring;
begin
   tt:=1;
   while (true) do
   begin
      readln(n);
      if n=0 then break;
      readln(a);  inc(n); a:=a+'.';
      i:=1;j:=0;next[1]:=0;
      while (i<=n-1) do
      begin
         if (j=0) or (a[i]=a[j]) then
         begin
             inc(i);inc(j);
           //  if (a[i]<>a[j]) then next[i]:=j else next[i]:=next[j];
             next[i]:=j;
         end
         else j:=next[j];
      end;

      writeln('Test case #',tt);

      for i:=2 to n do
      begin
         duan_luo:=i-next[i];
         if (duan_luo>0) and ((i-1) mod duan_luo=0) and ((i-1) div duan_luo>1) then
            writeln(i-1,' ',(i-1) div duan_luo);



      end;

//    readln;
      writeln;
      inc(tt);
   end;

end.


POJ 3080(最长公共子串)

Language:
Blue Jeans
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 8767   Accepted: 3688

Description

给出若干个基因串(由'A','T','S','C'构成),请找出最长公共子串。

Input

第一行为数据数。
对每组数据:第一行为字符串的个数m(2 <= m <= 10)
                        之后m行,每行一个基因串(有且仅有60个字母)

Output

对每组数据,找出最长公共子串,如果长度小于3,请输出 "no significant commonalities" ,否则输出最长的字符串,若有多个答案,输出字典序最小的。

Sample Input

3
2
GATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
3
GATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATA
GATACTAGATACTAGATACTAGATACTAAAGGAAAGGGAAAAGGGGAAAAAGGGGGAAAA
GATACCAGATACCAGATACCAGATACCAAAGGAAAGGGAAAAGGGGAAAAAGGGGGAAAA
3
CATCATCATCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
ACATCATCATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AACATCATCATTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT

Sample Output

no significant commonalities
AGATAC
CATCATCAT

Source

枚举字符串更新答案,用kmp算法进行匹配。


Program BlueJeans;
const
   maxn=60;
   maxm=10;
var
   tt,i,j,k,n,m:longint;
   a:array[1..maxm] of string;
   next:array[1..maxn] of longint;
   p,ans:string;
   flag:boolean;

function kmp(a,s:string):boolean;
var
   i,j,n:longint;
begin
   n:=length(a);

   j:=0; next[1]:=0; i:=1;
   while i<n do
   begin
      if (j=0) or (a[i]=a[j]) then
      begin
         inc(i); inc(j);
         if (a[i]<>a[j]) then next[i]:=j else next[i]:=next[j];
      end else j:=next[j];
   end;

   j:=0; i:=0;
   while (i<=maxn) and (j<=n) do
   begin
      if (j=0) or (a[j]=s[i]) then
      begin
         inc(i); inc(j);
  //       if (j=n) then exit(true);
      end else j:=next[j];
   end;

   if j>n then exit(true);
   exit(false);


end;
function compare(a,b:string):boolean;
var
   i,n,m:longint;
begin
   n:=length(a); m:=length(b);
   if (n<>m) then exit(n<m);
   for i:=1 to n do if (a[i]<>b[i]) then exit(ord(a[i])>ord(b[i]));
   exit(false);
end;
begin
   readln(tt);
   while (tt>0) do
   begin
      ans:='';
      readln(m);
      for i:=1 to m do readln(a[i]);

      for i:=1 to maxn do
         for j:=i to maxn do
         begin
            p:=copy(a[1],i,j-i+1);
            flag:=false;
            for k:=2 to m do
            begin
               if not(kmp(p,a[k])) then begin flag:=true; break; end;
            end;
            if not(flag) and (compare(ans,p)) then ans:=p;
         end;

      if length(ans)<3 then writeln('no significant commonalities')
      else writeln(ans);




      dec(tt);
   end;
end.

POJ 2752(不满足P[i]<>P[next[i]] 的next函数)

Language:
Seek the Name, Seek the Fame
Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 8682   Accepted: 4112

Description

给定一个字符串P,求它所有的满足P[1..i]到P[n-i+1..n]的i.

Input

数据有若干行
每行为一组数据P,1 <= Length of P <= 400000. 

Output

每行一组数据,按数字升序输出所有的i. 

Sample Input

ababcababababcabab
aaaaa

Sample Output

2 4 9 18
1 2 3 4 5

Source


要考虑KMP中Next函数的性质-P[next[i]]<>P[i]

如果这个条件不满足呢?

先在字符串后插入一个'.',表示'.'前的循环,这样就能规避这一点(因为‘.’不会与任何字母重合)

再把后面那个if从句删掉(它只能跳过一些-非全部的-P[next[i]]<>P[i]的情况,因为.后面可能出现两次'a'

这样就可以了,最后把答案(由于不考虑a[i],只考虑前面循环节的长度,故-1)


Program SeekName;
const
    maxn=400000;
var
   n,i,j,size:longint;
   a:ansistring;
   ans,next:array[1..maxn] of longint;

begin
   while not seekeof do
   begin
      readln(a); a:=a+'.';
      n:=length(a);
      j:=0;next[1]:=0;i:=1;
      while (i<n) do
      begin
         if (j=0) or (a[i]=a[j]) then
         begin
            inc(i);
            inc(j);
         //   if (a[i]<>a[j]) then next[i]:=j else next[i]:=next[j];
            next[i]:=j;
         end else j:=next[j];
      end;
      size:=0; j:=n;
      repeat
         inc(size);
         ans[size]:=j-1;
         j:=next[j];
      until j<=1;


      write(ans[size]);
      for i:=size-1 downto 1 do write(' ',ans[i]);
      writeln;



   end;

end.