1. 程式人生 > >C. Two strings-二分的威力,思路題

C. Two strings-二分的威力,思路題

Educational Codeforces Round 17 C. Two strings
題意,給兩個長度小於十萬的字串A、B,B刪除連續的字串後成為A的子串(不需要連續),求這最長子串,可能為空也可能為A本身。
思路:列舉B的子串其實只要列舉刪除的位置就好了,因為B刪除的部分是連續的字串而且只能刪一次。可以把字串B看成三部分。

                    | 左邊部分 || 刪除部分 || 右邊部分 |

左邊部分、右邊部分可能為空。
其實就是前後綴,先求出B各個字首需要A字首的長度,再求出B各個字尾需要A字尾的長度,

比如說字串A是 abacaba,字串B是 abcdcba ,B的字首依次是a、ab、abc、abcd….,字尾依次是a、ba、cba、dcba、cdcba….,字首a需要字串A的字首長度為1(a),字首ab需要字串A的字首長度為2(ab),字首abc需要字串A的字首長度為4(abac)….

字尾同理,那麼,只需要滿足從b前、字尾數組裡面選出兩個位置相加要求長度小於等於字串A長度,這兩個位置就能確定刪除部分了。用len來表示B刪除連續字串後剩下的長度。

先貼一下我test 36 TLE的程式碼,原因是輪詢太慢,2000ms,

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long int ll;
const int maxn = 100007;
char a[maxn],b[maxn];
int lena,lenb;
int
needApreLen[maxn],needAsufLen[maxn]; int main() { // freopen("in.txt","r",stdin); scanf("%s",a); scanf("%s",b); lena = strlen(a); lenb = strlen(b); for(int i=0;i<maxn;++i)needApreLen[i] = lena + 5; for(int i=0;i<maxn;++i)needAsufLen[i] = lena + 5; int pos = 0; for
(int i = 0; i < lenb;++i) { while(pos<lena && a[pos] != b[i])++pos; ++pos; //index starts from 0,plus before update if(pos<=lena)needApreLen[i] = min(pos,needAsufLen[i]); if(pos>lena)break; } pos = lena - 1; for(int i = lenb-1;i >= 0;--i) { while(pos>=0 && a[pos] != b[i])--pos; if(pos>=0)needAsufLen[i] = min(lena - pos,needAsufLen[i]); --pos; //index starts from lena-1,decrease after update if(pos<0)break; } //for(int i = 0;i < lenb;++i)printf("%d ",needApreLen[i]);printf("\n"); //for(int i = 0;i < lenb;++i)printf("%d ",needAsufLen[i]);printf("\n"); int longestL = -1,longestR = -1; for(int i = 0;i < lenb;++i)if( needApreLen[i] <= lena )longestL = i; for(int i = lenb - 1;i >= 0;--i)if( needAsufLen[i] <= lena )longestR = i; if(longestL == -1 && longestR == -1)printf("-"); else if(longestL == -1){//only suffix for(int i=longestR;i<lenb;++i)printf("%c",b[i]); }else if(longestR ==-1){//only prefix for(int i=0;i<=longestL;++i)printf("%c",b[i]); } else{ int len,left,right,op=0,os=0; if(longestL+1 > lenb-longestR){ //only prefix len = longestL+1; left = longestL; op = 1; }else{ //only suffix len = lenb-longestR; right = longestR; os = 1; } // printf("lenb = %d\n",lenb); // printf("longestL = %d longestR = %d\n",longestL,longestR); // printf("len = %d\n",len); for(int i = longestL,j = longestR;i>=0;--i)//i : max len of valid prefix,j : max len of valid suffix { j = longestR; while(j<lenb &&needApreLen[i] + needAsufLen[j] > lena)++j; if(j<lenb && i<j && lenb-(j-i-1)>len){ left = i; right = j; len = lenb-(j-i-1); os = op = 0; } } if(os==0 && op==0){ // printf("here1\n"); for(int i=0;i<=left;++i)printf("%c",b[i]); for(int i=right;i<lenb;++i)printf("%c",b[i]); }else if(op){ // printf("here2\n"); for(int i=0;i<=left;++i)printf("%c",b[i]); }else if(os){ // printf("here3\n"); for(int i=right;i<lenb;++i)printf("%c",b[i]); } } printf("\n"); return 0; }

後來用二分查詢重新優化了31ms過。。。二分真的很快,但是upper_bound用起來需要注意是否越界,而且需要先排序,而且處理字串,從1開始存的話方便很多,不要怕錯,用多了就會習慣了。重寫幾次程式碼也是很考毅力啊,可是做題不A等於沒做,一定要寫出來,況且根本就沒有努力到需要講求天賦的水平,優秀的人只是願意比你多試幾次而已,一點點的積累很重要,一天一點,一年會改變很多。沒天賦的人需要努力,有天賦的就更需要了,不然浪費。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long int ll;
const int maxn = 100050;
char a[maxn],b[maxn];
int lena,lenb;
int needAP[maxn],needAS[maxn];
int main()
{
    //freopen("in.txt","r",stdin);
    scanf("%s",a+1);
    scanf("%s",b+1);
    lena = strlen(a+1);
    lenb = strlen(b+1);
    for(int i=1;i<=lenb;++i)needAP[i] = needAS[i] = lena + 1;

    int pos = 1,longestL = lenb,longestR = 1;
    for(int i = 1; i <= lenb;++i){
        while(pos<=lena && a[pos] != b[i])++pos;
        if(pos<=lena)needAP[i] = pos;
        else {longestL = i-1;break;}
        ++pos;
    }

    pos = lena;
    for(int i = lenb;i >= 1;--i){
        while(pos>=1 && a[pos] != b[i])--pos;       
        if(pos>=1)needAS[i] = lena - pos + 1;
        else {longestR = i+1;break;}
        --pos;      
    }

    if(longestL < 1 && longestR > lenb)printf("-");
    else if(longestL == lenb || longestR == 1)printf("%s",b+1);
    else {
        int len,left,right;
        if(longestL > lenb - longestR + 1){//only prefix
            len = longestL;
            left = longestL;
            right = lenb + 1;
        }else{                              //only suffix
            len = lenb - longestR + 1;
            left = 0;
            right = longestR;
        }

        int pfind = 0;
        for(int i=longestR;i<=lenb;++i)
        {
            pfind = upper_bound(needAP,needAP+longestL+2,lena - needAS[i]) - needAP - 1;
            if(pfind <= longestL && pfind + lenb-i+1 > len){
                left = pfind;
                right = i;
                len = pfind + lenb-i+1;
            }
        }

        for(int i = 1;i <= left;++i)printf("%c",b[i]);
        for(int i = right; i<= lenb;++i)printf("%c",b[i]);
    }   
    printf("\n");
    return 0;
}