Description
wyl8899今天也很刻苦的在做老师布置下来的题目!
这一天老师布置的题目是这样的:
给出两个仅含小写字母的字符串A和B,输出最大的k,使得A[1..k]是B的子串。
A和B的长度都不会超过50000。
很显然他并不知道正确的做法,但是他居然卡着时间过掉了老师给的数据!
你找到了他提交给老师的程序,经过测试你惊讶的发现,他的程序运行时间恰好是最终答案,单位是毫秒。
你现在找到了老师给的数据中的一笔,你决定至多修改A串中的一个字母,使得他的程序尽可能的慢。
现在,你能告诉我修改数据之后他的程序在这个测试点的运行时间么?(单位当然还是毫秒)
Input
两行,每行一个仅含小写字母的字符串,分别是A和B。
保证A和B的长度都不超过50000。
Output
你修改数据之后,wyl8899的程序在这个测试点的运行时间,单位是毫秒。
Sample Input
输入1:
adbc
aabbabd
输入2:
aab
aabcc
Sample Output
输出1:
3
输出2:
3
Data Constraint
保证A和B的长度都不超过50000。
Hint
数据是在Windows下生成的,测评环境是Linux。
由于换行符的差异,对于C/C++选手,请不要使用gets(s)读入一行字符,建议使用scanf("%s",s)的形式。
Pascal选手直接使用readln()读入一行即可,理论上readln()不会受到不同换行符的影响。
题解
这题真妙♂
0~100%
有两种方法,你没有听错,有两种方法。
第一种:暴力。
我们直接枚举当前把第i个位置的字符替换。
(O(n^2))求解即可。
加上一个剪枝就好了——把相同且连续的字符缩在一起。
比如aabbbbccccd这样,用一个next来记录当前第i个位置最近的下一位与之不同的字符的位置。
似乎出题人也没有想到要卡掉这种方法。(滑稽)
第二种:错误的DP。
设(f_{[i,0/1]})表示当前第i个位置,是否用掉过替换的机会的答案。
随意转移。当然,正确性显然错误。
然鹅数据还是放某些人过了或拿到97分。
100%
有超级多种解法
其实都是一个思想,做的时候各有不同罢了。
一个思路——枚举当前目标字符串中的开头位置,然后可以得到从当前位置开始不替换所能达到的最远位置。
那么考虑把这个最远位置给替换后,再往后继续拓展。
一种做法(当然是最优秀的做法)
我们处理出LCP(最长公共前缀)
然后利用LCP来做上面的东西即可。
这个LCP可以利用exkmp或后缀自动机或后缀数组来求。
另一种做法(比较容易卡掉)
我们考虑hash。
首先枚举开头位置,然后二分加上hash判定即可得到最远位置。
其次,我们还是利用二分继续往右边拓展。
时间复杂度是(O(m log m))的。
只是hash可能要用一个三重hash。
一个是上面做法优化版的做法
我们发现,在求最远位置的时候,前面有一大段都是匹配的。
而且,这一大段都是以匹配字符串第1位开始的。
这不就是kmp失配时的情况吗?
我们直接做一遍kmp,然后如果在失配的时候,再利用二分往后面拓展即可。
这样,我们就连hash都只需要用一重即可。
标程
uses math;
var
i,j,k,l,r,mid,n,m,now,ans:longint;
xx,yy,ad:int64;
s,t:ansistring;
next:array[0..500000] of longint;
mi,z,o:array[0..500000] of int64;
p:int64=29;
mo:int64=1000000007;
//hs:array[0..1000000007] of boolean;
begin
//assign(input,'1data.in');reset(input);
mi[0]:=1;
for i:=1 to 500000 do mi[i]:=mi[i-1]*p mod mo;
readln(s);
readln(t);
t:=t+'{';
n:=length(s);
m:=length(t);
z[1]:=ord(s[1])-96;o[1]:=ord(t[1])-96;
for i:=2 to n do z[i]:=(z[i-1]*p+ord(s[i])-96) mod mo;
{ for i:=1 to n do
begin
for j:=i to n do
begin
xx:=(z[j]-z[i-1]*mi[j-i] mod mo+mo) mod mo;
hs[xx]:=true;
end;
end; }
for i:=2 to m do o[i]:=(o[i-1]*p+ord(t[i])-96) mod mo;
j:=0;
for i:=2 to n do
begin
while (j<>0) and (s[j+1]<>s[i]) do j:=next[j];
if s[j+1]=s[i] then j:=j+1;
next[i]:=j;
end;
j:=0;
for i:=1 to m-1 do
begin
if j=n then
begin
writeln(n);
halt;
end;
j:=j;
if (j=0) and (t[i]<>s[j+1]) then
begin
l:=i;
r:=m;
k:=0;
while l<=r do
begin
mid:=(l+r) div 2;
xx:=(z[j+1+mid-i]-z[j+1]*mi[mid-i] mod mo+mo) mod mo;
yy:=(o[mid]-o[i]*mi[mid-i] mod mo+mo) mod mo;
if xx=yy then
begin
k:=mid;
l:=mid+1;
end
else r:=mid-1;
end;
ans:=max(ans,k-i+j+1);
end;
while (j>0) and (t[i]<>s[j+1]) do
begin
l:=i;
r:=m;
k:=0;
while l<=r do
begin
mid:=(l+r) div 2;
xx:=(z[j+1+mid-i]-z[j+1]*mi[mid-i] mod mo+mo) mod mo;
yy:=(o[mid]-o[i]*mi[mid-i] mod mo+mo) mod mo;
if xx=yy then
begin
k:=mid;
l:=mid+1;
end
else r:=mid-1;
end;
ans:=max(ans,k-i+j+1);
j:=next[j];
end;
if t[i]=s[j+1] then inc(j);
if j=m then j:=next[m];
ans:=max(ans,j);
end;
writeln(ans);
end.
end.