zoukankan      html  css  js  c++  java
  • 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.
    
    
    
  • 相关阅读:
    Last_IO_Error: Got fatal error 1236 from master when reading data from binary log: 'Could not find first log file name in binary log index file'
    ptconfigdiff的使用
    freebsd上安装sudo
    vm9.02的序列号
    pttablechecksum
    "Makefile", line 3: Need an operator
    nc的使用
    vs2005自带的水晶报表破解方法
    [vs2008环境]绑定水晶报表的两种方式(Pull和Push)
    .NET环境下水晶报表使用总结
  • 原文地址:https://www.cnblogs.com/RainbowCrown/p/11160636.html
Copyright © 2011-2022 走看看