HRBUST2343 巴啦啦能量(字符串,技巧)

Description

话说上回涂山小八进入“时空之门”,却不是回到现实世界,而是一间方正小屋。随着时空之门缓缓关上,小八才看清墙上排着很多奇怪的字符。正奇怪着,小屋内响起了说话声。

“你现在本应已回到现实世界,但你来的时间太久,能量已经用光,无法直接通过时空之门,所以才会来到此地。唯今之计只有获得巴啦啦能量,否则你依然会被永远留在此地。”

“如何获得巴啦啦能量?”

“看见墙上的巴啦啦密文了吗?我现在给你一个能量之匙,从巴啦啦密文中找出巴啦啦能量串即可获得巴啦啦能量。巴啦啦能量串是所有包含能量之匙的最小串,切记,若找到多个符合要求的巴啦啦能量串,不可贪多,只需带走第一个,否则前功尽弃。”

Input

输入数据有多组,每组数据输入第一行输入字符串巴啦啦密文S,第二行输入字符串能量之匙T,S长度lens(1≤lens≤105
),T长度lent(1≤lent≤105 )(输入不包含空格),输入字符区分大小写。

Output

对于每组输入数据,输出找到的巴啦啦能量串,每组输出占一行。如果找不到巴啦啦能量串,输出一个空行。

Sample Input

ADOBECODEBANC

ABC

ABCDA

BD

Sample Output

BANC

BCD

思路

给出两个字符串 S 和 T, 询问 S 中长度最小的位置最靠前
的包含 T 的子串, 如果不存在, 只输出换行

首先 O(N2) 复杂度肯定过不了,我们得找一找优化的方法

用两个标记数组分别存储T串中的字符出现的次数和是否出现过
用l记录当前的左端点的位置,minl记录更新后的左端点的位置,minn记录要求的子串的长度,cnt记录当前已经出现了几个T串中的字符

然后遍历S串,跳过没有出现过的字符,碰见出现过的字符,就给他的出现次数-1,当减一之后的然后判断当前的出现次数是否>=0,如果是的话就用cnt记录一下,当cnt的数量和T串的长度一样时,代表当前已经有一个区间包含这个T串了,我们根据当前的左右端点尝试更新区间,然后将左边界右移,在右移左边界的过程中如果碰见已经在T串出现过的字符,就给他的出现次数+1,当他的出现次数加到正数时,cnt的值减一(当前我们的区间已经不包含T串中的所有字符了),所以右移左边界停止,继续在右边界找,直到包含整个T串,然后再尝试更新左边界。。。
总体的思路就是,枚举包含T串的区间,然后更新边界,找出最小的

这样,就以线性的复杂度求出要求的子串,最后输出找到的区间的串就是答案

还可以用STL的map做,思路是一样的

代码1

#include <cstdio>
#include <cstring>
#include <cctype>
#include <string>
#include <set>
#include <iostream>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <algorithm>
#define mem(a,b) memset(a,b,sizeof(a))
#define inf 0x3f3f3f3f
using namespace std;
const int N=1e5+20;
char s[N],t[N];
int vis[200];//标记出现的次数
int sear[200];//标记是否出现过
int main()
{
    while(~scanf("%s%s",s,t))
    {
        mem(vis,0);
        mem(sear,0);
        int tlen=strlen(t);
        int slen=strlen(s);
        for(int i=0; i<tlen; i++)
        {
            vis[t[i]]++;
            sear[t[i]]=1;
        }
        int cnt=0,l=0,minl=0,minn=slen+1;
        for(int r=0; r<slen; r++)
            if(sear[s[r]])
            {
                if(--vis[s[r]]>=0)
                    cnt++;
                while(cnt==tlen)
                {
                    if(r-l+1<minn)
                        minl=l,minn=r-l+1;
                    if(sear[s[l]])
                        if(++vis[s[l]]>0)
                            cnt--;
                    l++;
                }
            }
        if(minn>slen) printf("\n");
        else
        {
            for(int i=minl; i<minl+minn; i++)
                printf("%c",s[i]);
            printf("\n");
        }
    }
    return 0;
}

代码2

#include <stdio.h>
#include <iostream>
#include <string>
#include <map>
using namespace std;
string minWindow(string S, string T)
{
    map<char, int> m;
    for(int i = 0; i < T.size(); ++ i)
         m[T[i]]++;
    int cnt = 0, l = 0, minl = 0, minsize = S.size() + 1;
    for(int r = 0; r < S.size(); ++ r)
        if(m.find(S[r]) != m.end())
        {
            if(-- m[S[r]] >= 0)
                ++ cnt;
            while(cnt == T.size())
            {
                if(r - l + 1 < minsize)
                    minl = l, minsize = r - l + 1;
                if(m.find(S[l]) != m.end())
                    if(++ m[S[l]] > 0)
                        -- cnt;
                ++ l;
            }
        }
    if(minsize>S.size())
        return "";
    return S.substr(minl, minsize);
}
int main()
{
    string s, t;
    while(cin >> s >> t)
    {
        string ans = minWindow(s, t);
        cout << ans << endl;
    }
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值