Two strings CodeForces - 762C(二分+预处理)

You are given two strings a and b. You have to remove the minimum possible number of consecutive (standing one after another) characters from string b in such a way that it becomes a subsequence of string a. It can happen that you will not need to remove any characters at all, or maybe you will have to remove all of the characters from b and make it empty.

Subsequence of string s is any such string that can be obtained by erasing zero or more characters (not necessarily consecutive) from string s.

Input
The first line contains string a, and the second line — string b. Both of these strings are nonempty and consist of lowercase letters of English alphabet. The length of each string is no bigger than 10^5 characters.

Output
On the first line output a subsequence of string a, obtained from b by erasing the minimum number of consecutive characters.

If the answer consists of zero characters, output «-» (a minus sign).

Example

Input
hi
bob
Output
-
Input
abca
accepted
Output
ac

Input
abacaba
abcdcba
Output
abcba

Note
In the first example strings a and b don’t share any symbols, so the longest string that you can get is empty.

In the second example ac is a subsequence of a, and at the same time you can obtain it by erasing consecutive symbols cepted from string b.

大致题意:给你两个串s1,s2,你可以删除s2中连续的一段字符,使得剩下的s2串拼接起来成为s1的子序列串。如果存在,输出拼接后s2最长的那种情况,否则将s2串删光,输出-。

思路:假设我们要删除的连续字符的长度为len,len的范围为0到l2(s2串的长度)。可以很容易的看出len具有单调性,所以我们可以二分len,然后枚举所要删除的连续字符的起点,这样的话时间复杂度为nlogn,然后考虑怎么check,如果每次都直接暴力去判断的话那么需要o(n)的时间复杂度,这样总的时间复杂度就达到了n^2logn,T掉了。考虑到每次删完剩下的两段可以看作是s2串的前缀和后缀,所以我们可以先预处理出s2的所有前缀在s1串中最靠前的子序列的末尾位置a,和s2的所有后缀在s1串中最靠后的子序列的开头位置b,存到两个数组中,每次只需比较前者和后者的位置a,b是否满足a < b即可。这样check的时间复杂度就降到了O(1),总的时间复杂度为nlogn。

代码如下

#include <iostream>  
#include <cstdio>  
#include <cstring>  
#include <cmath>  
#include <algorithm>
#include <vector>
#include <map>
#include <bitset>
using namespace std;  
const int maxn=1e5+5;
#define ll long long int 

char s1[maxn],s2[maxn];
int bef[maxn],nex[maxn];//前缀,后缀。
int l1,l2;
int ans1,ans2;//分别记录删除的起点和长度
int check(int len)
{
    for(int i=0;i<=l2-len;i++)
    {
        if(i==0)//特判一下如果从第一个就开始删
        {
            if(nex[len]>=0)//此时只要后缀满足是s1的子序列就可以了
            {
                ans1=0;
                ans2=len;
                return 1;
             } 
        }
        else
        {
            if(bef[i-1]<nex[i+len])//否则需要满足前缀位置小于后缀位置
            {
                ans1=i;
                ans2=len;
                return 1;
             } 
        } 
    }
    return 0;
}
int main()  
{  
    gets(s1);
    gets(s2);
    l1=strlen(s1);
    l2=strlen(s2);
    int j=0;
    for(int i=0;i<l2;i++)//前缀 
    {
        while(1){
            if(j==l1)//如果此时的前缀不是s1的子序列,则赋值为l1,判断的时候必将会大于后缀位置
            {
                bef[i]=j;
                break;
            }
            if(s2[i]==s1[j])
            {
                bef[i]=j;
                j++;
                break;
            }
            j++;
        }
    }
    j=l1-1;
    for(int i=l2-1;i>=0;i--)//后缀 
    {
        while(1){
            if(j==-1)//如果此时的后缀不是s1的子序列,则赋值为-1,判断的时候必将会小于前缀位置
            {
                nex[i]=j;
                break;
            }
            if(s2[i]==s1[j])
            {
                nex[i]=j;
                j--;
                break;
            }
            j--;
        }
    }
    nex[l2]=l1;//可能会把后缀删光

    int l=0,r=l2-1;
    ans1=-1;
    while(r>=l)
    {
        int mid=(r+l)/2;
        if(check(mid))
        {
            r=mid-1;
        }
        else
        l=mid+1;
    }
    if(ans1==-1)
    printf("-\n");
    else 
    {
        for(int i=0;i<ans1;i++)
        printf("%c",s2[i]);
        for(int i=ans1+ans2;i<l2;i++)
        printf("%c",s2[i]);

    }

    return 0;  
}  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值