Codeforces 762C Two strings

原文链接: Codeforces 762C Two strings

上一篇: POJ 3977 折半枚举+二分搜索

下一篇: CodeForces 797C

• 给定两个字符串A,B
• 再B中删除最少的连续字符(一段字符),使得B成为A的子序列
• 1 ≤ |A|, |B| ≤ 1e5

二分+预处理前缀和后缀

#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <algorithm>
#include <cmath>
#include <vector>
#include <cstring>
#include <stdio.h>

using namespace std;

typedef long long ll;
const int MAXN = 1e5 + 155;
int pre[MAXN], suf[MAXN];//存前缀和后缀    相当于存 A的  对于B的子序列的长度


char a[MAXN], b[MAXN];
int main(){
    while(~scanf("%s%s", a + 1, b + 1)){
        int len1 = strlen(a + 1);
        int len2 = strlen(b + 1);
        int last = 0;
        // 不论是前缀还是后缀, 只要A 中有B的 某些串就可以 
        //可以不连续,  例如A :abc   B: ac   B为A的子串
        for(int i = 1 ; i <= len1 ; i++) //赋初值
            pre[i] = len2, suf[i] = 1;
        suf[len1 + 1] = len2 + 1;// 必须有 目的是为了删除子串的作用, pre[i-1]
        for(int i = 1, j = 1 ; i <= len1 && j <= len2 ; i++){//这里是构造A的前缀
            if(a[i] == b[j]){// 相同记录前缀, 更新
                pre[i] = j;
                last = j;
                j++;
            } else
                pre[i] = last; //如果这个不相同,就等于前面的last
        }
        last = len2 + 1;
        for(int i = len1, j = len2 ; i >= 1 && j >= 1 ; i--){//构造A的后缀
            if(a[i] == b[j]){
                suf[i] = j;
                last = j;
                j--;
            } else
                suf[i] = last;
        }
        int ans = len2 + 1, left = 1, right = len2 + 1;//初始化答案为最大;
        for(int i = 1 ; i <= len1 + 1 ; i++){//枚举所有的情况
            if(pre[i - 1] < suf[i] && ans > suf[i] - pre[i - 1]){
                ans = suf[i] - pre[i - 1];
                left = pre[i - 1];
                right = suf[i];
            }
        }
        if(ans == len2 + 1){
            cout << "-" << endl;
            continue;
        }
        for(int i = 1 ; i <= len2 ; i++){// 输出 删除后的b
            if(i <= left || i >= right)
                cout << b[i];
        }
    }
    return 0;
}

题目大意:

给定两个字符串a,b 16000219_gvlU.png16000219_QKfV.png16000219_RlYt.png16000219_r9gY.png16000219_VpkU.png16000219_SL8H.png16000219_hePH.png16000219_nTzl.png16000219_05yb.png (len≤105),让你去b中的一个连续的字段,使剩余的b串中的拼接起来的两个串是a穿的子序列。最大化这个字串的长度。

题解:

删除这个操作不太好说,我们先换一个思路:实际上删除就是在b串中分别取出两个不相交的前缀和后缀,使得这两个串在a串中不重叠地出现
我们发现答案具有显然的单调性,所以我们首先二分答案(二分删去的字串长度)。
现在来考虑如何进行判定:
一个很直接的思路就是枚举所有的符合条件的前缀和后缀
然后对这个前缀求其在a中的最靠左的子序列的右端下标。
相应的后缀也这么处理.
如: a = "abbcbc",pre = "abc" [数组下标从1开始]
那么串pre,在a中最靠左的子序列有段下标为4.
那么我们我们只要找到一个点,从这个点劈开后,前缀的下标 < 后继的下标即可
枚举 16000219_XEwM.png16000219_gvlU.png16000219_r9gY.png16000219_05yb.png O(n)求出在a序列中拓展到的位置 16000219_XEwM.png16000219_gvlU.png16000219_r9gY.png16000219_05yb.png O(n),总时间复杂度为 16000219_XEwM.png16000219_gvlU.png16000219_r9gY.png16000219_1CU7.png16000219_QKfV.png16000219_k6r3.png16000219_89PM.png16000219_r9gY.png16000219_05yb.png O(n2logn)
TLE
我们发现每次对一个前缀求在a中出现的下标都是重复的问题
所以我们 16000219_XEwM.png16000219_gvlU.png16000219_r9gY.png16000219_05yb.png O(n)预处理一遍(后缀是类似的)
所以我们就做到了 16000219_XEwM.png16000219_gvlU.png16000219_r9gY.png16000219_05yb.png O(n)判定
时间复杂度 16000219_XEwM.png16000219_gvlU.png16000219_r9gY.png16000219_QKfV.png16000219_k6r3.png16000219_89PM.png16000219_r9gY.png16000219_05yb.png

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
 
const int maxn = 200010;
char a[maxn], b[maxn];
int pre[maxn], suf[maxn];
int main(){
    scanf("%s%s", a + 1, b + 1);
    int n = strlen(a + 1);
    int m = strlen(b + 1);
    for(int i = 1, j = 0 ; b[i] ; ++i){
        ++j;
        while(b[i] != a[j] && j <= n) ++j;
        pre[i] = j;
    }
    for(int i = m, j = n + 1 ; b[i] ; --i){
        --j;
        while(b[i] != a[j] && j > 0) --j;
        suf[i] = j;
    }
    suf[m + 1] = n + 1;
    int ans1, ans2, l = 0, r = m;
    while(l <= r){
        int mid = l + r >> 1, i;
        for(i = 0 ; i + mid <= m ; ++i) if(pre[i] < suf[i + mid + 1]) break;
        if(i + mid <= m){
            ans1 = i, ans2 = i + mid + 1, r = mid - 1;
        } else l = mid + 1;
    }
    if(ans2 - ans1 > m) putchar('-');
    else {
        for(int i = 1 ; i <= ans1 ; ++i) putchar(b[i]);
        for(int i = ans2 ; b[i] ; ++i) putchar(b[i]);
    }
    getchar(); getchar();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值