C. Two strings-二分的威力,思路題
阿新 • • 發佈:2018-12-23
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;
}