jzoj3337. wyl8899的TLE

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.


posted @ 2019-07-09 21:34  RainbowCrown  阅读(219)  评论(0编辑  收藏  举报