HDU 4302(zkw线段树-单点修改区间最值)

Holedox Eating

Problem Description
Holedox在一条长度为L的线上. Holedox能在线上移动,线上会时不时的出现蛋糕(保证是整点)。Holedox会在想吃蛋糕时去最近的有蛋糕的点吃,如果左右的最近蛋糕与它的距离相等,它会按上一次行走的方向,如果没有蛋糕,它会呆在原地。
 


Input
第一行为数据组数T (1 <= T <= 10)
对每组数据,第一行有2个整数L,n(1<=L,n<=100000)表示线段长度和事件数。
接下来n行,每行一个事件:‘0 x'表示在x的位置出现一块蛋糕,’1‘表示Holedox去吃1块蛋糕
Holedox一开始在0点。
 


Output
对每组数据,输出一行‘Case i: dis',dis表示Holedox的移动距离。
 


Sample Input
3 10 8 0 1 0 5 1 0 2 0 0 1 1 1 10 7 0 1 0 5 1 0 2 0 0 1 1 10 8 0 1 0 1 0 5 1 0 2 0 0 1 1
 


Sample Output
Case 1: 9 Case 2: 4 Case 3: 2
 

这题是单点修改+区间最值的zkw线段树(a[]表示数量)

这题有几个技巧:

1.线段树多开;

2.'0'点的包含(把所有数字+1,显然不影响Holedox的移动距离)

3.额外信息的记录(为了把这题转换成线段树)


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<iostream>
using namespace std;
#define MAXT (10+10)
#define MAXN (100000+10)
#define MAXm (100000+10)
#define INF (2139062143)
int n,m,M,T,now,direction,ans;
int t[2][MAXN*10];  //0->max 1->min
int a[MAXN];
void dec(int x)
{
	a[x]--;
	if (a[x]==0)
	{
		x+=M;
		t[0][x]=0;t[1][x]=INF;
		for (x>>=1;x;x>>=1)
		{
			t[0][x]=max(t[0][x<<1],t[0][(x<<1)^1]);
			t[1][x]=min(t[1][x<<1],t[1][(x<<1)^1]);
		}
	}
}
void go_left(int Lans)
{
	ans+=now-Lans;
	now=Lans;direction=-1;
	dec(now);
}
void go_right(int Rans)
{
	ans+=Rans-now;
	now=Rans;direction=1;
	dec(now);
}
int main()
{
	freopen("Holding.in","r",stdin);
	scanf("%d",&T);
	for (int k=1;k<=T;k++)
	{
		memset(t[0],0,sizeof(t[0]));ans=0;
		memset(t[1],127,sizeof(t[1]));
		memset(a,0,sizeof(a));now=1;direction=1; //1->right -1->left
		scanf("%d%d",&n,&m);n+=2;
		M=1;while (M-2<n) M<<=1;
		for (int i=1;i<=m;i++)
		{
			int p1,p2;
			scanf("%d",&p1);
			if (p1==0)
			{
				scanf("%d",&p2);p2+=1;
				a[p2]++;
				if (a[p2]==1)
				{
					p2+=M;t[0][p2]=t[1][p2]=p2-M;
					int p=p2;
					for (p>>=1;p;p>>=1) t[0][p]=max(t[0][p<<1],t[0][(p<<1)^1]);
					for (p2>>=1;p2;p2>>=1) t[1][p2]=min(t[1][p2<<1],t[1][(p2<<1)^1]);
				}
			}
			else
			{
				//(0,now+1)->[1,now]
				int l=M,r=now+1+M,Lans=0,Rans=INF;
				while (l^r^1)
				{
					if (~l&1) Lans=max(Lans,t[0][l+1]);
					if (r&1) Lans=max(Lans,t[0][r-1]);
					l>>=1;r>>=1;
				}
				//(now-1,n)->[now,n-1]
				l=now-1+M;r=n+M;
				while (l^r^1)
				{
					if (~l&1) Rans=min(Rans,t[1][l+1]);
					if (r&1) Rans=min(Rans,t[1][r-1]);
					l>>=1;r>>=1;
				}
				if (Lans==0&&Rans==INF) continue;
				else if (Lans==0) go_right(Rans);
				else if (Rans==INF) go_left(Lans);
				else if (now-Lans<Rans-now) go_left(Lans);
				else if (now-Lans>Rans-now) go_right(Rans);
				else if (now-Lans==0) dec(now);
				else if (direction==1) go_right(Rans);
				else go_left(Lans);




			}
			/*
				for (int j=1;j<=M*2;j++) if (t[0][j]) cout<<j<<":"<<t[0][j]<<' ';
				cout<<endl;
				for (int j=1;j<=M*2;j++) if (t[1][j]<INF) cout<<j<<":"<<t[1][j]<<' ';
				cout<<endl;
			*/




		}
		printf("Case %d: %dn",k,ans);

	}
	return 0;
}

POJ 2155(二维zkw线段树)

Language:
Matrix
Time Limit: 3000MS   Memory Limit: 65536K
Total Submissions: 13137   Accepted: 4948

Description

给定一个矩阵(初始为0),维护2个操作:
1. C x1 y1 x2 y2 (1 <= x1 <= x2 <= n, 1 <= y1 <= y2 <= n) ,以(x1,y1)为左上角,(x2,y2)为右上角的矩阵取反。
2. Q x y (1 <= x, y <= n) 输出(x,y)的状态

Input

第一行为数据组数X (X <= 10)
每组数据第一行为N,T (2 <= N <= 1000, 1 <= T <= 50000) 表示矩阵边长与操作次数。
接下来T行,为Q或C操作 

Output

请输出所有提问结果。
每组数据后一回车。 

Sample Input

1
2 10
C 2 1 2 2
Q 2 2
C 2 1 2 1
Q 1 1
C 1 1 2 1
C 1 2 1 2
C 1 1 2 2
Q 1 1
C 1 1 2 1
Q 2 1

Sample Output

1
0
0
1

Source

POJ Monthly,Lou Tiancheng

这题是二维的线段树,

先建一棵线段树,再将它的所有结点(包括根)建立一个二叉树(维护这个结点)

异或仅需记录标记某点所对应的区间取反

查找时统计共记了几个标记即可(标记永久化)


Program Matrix;
const
   maxtt=10;
   maxn=1000;
   maxMM=50000;
var
   tt,n,mm,i,j,k,x1,y1,x2,y2,x,y,p1,p2,ans:longint;
   c:char;
   t:array[1..5000,1..5000] of boolean;
   M:longint;

Procedure change_y(x,y1,y2:longint);
var
   i,j:longint;
begin
   dec(y1);inc(y2);
   inc(y1,M);inc(y2,M);
   while ((y1 xor y2 xor 1)>0) do
   begin
      if ((y1 and 1)=0) then t[x,y1+1]:=not(t[x,y1+1]);
      if ((y2 and 1)=1) then t[x,y2-1]:=not(t[x,y2-1]);
      y1:=y1 shr 1;y2:=y2 shr 1;
   end;

end;

Procedure change_x(x1,y1,x2,y2:longint);
var
   i,j:longint;
begin
   dec(x1);inc(x2);
   inc(x1,M);inc(x2,M);
   while ((x1 xor x2 xor 1)>0) do
   begin
      if ((x1 and 1)=0) then change_y(x1+1,y1,y2);
      if ((x2 and 1)=1) then change_y(x2-1,y1,y2);
      x1:=x1 shr 1;x2:=x2 shr 1;
   end;
end;

function find_y(x,y:longint):boolean;
var
   i,j:longint;
begin
   inc(y,M); find_y:=false;
   while (y>0) do
   begin
      if (t[x,y]) then find_y:=not(find_y);
      y:=y shr 1;
   end;

end;
function find_x(x,y:longint):boolean;
var
   i,j:longint;
begin
   inc(x,M); find_x:=false;
   while (x>0) do
   begin
      find_x:=find_x xor find_y(x,y);
      x:=x shr 1;
   end;
end;
begin
   readln(tt);
   while (tt>0) do
   begin
      fillchar(t,sizeof(t),false);
      readln(n,mm);
      M:=1;
      while (M-2<n) do M:=M shl 1;
      for k:=1 to mm do
      begin
         read(c);
         if c='C' then
         begin
            readln(x1,y1,x2,y2);
            change_x(x1,y1,x2,y2);





         end
         else
         begin     //Q
            readln(x1,y1);
            if (find_x(x1,y1)) then writeln('1') else writeln('0');


         end;



      end;
      dec(tt);
      if (tt>0) then writeln;
   end;
end.

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.