XMU 1431 字符串的运算 字符串的的次方中的子串中找 目标串

1431.字符串的运算
Time Limit: 1500 MS         Memory Limit: 65536 K
Total Submissions: 137 (36 users)         Accepted: 36 (26 users)

Description

我们对字符串 S 做了以下定义:
1. S ^ k表示由k个字符串S构成的新字符串。 例如, S = "abc", k = 3, 则S ^ k  =  "abcabcabc"
2. Subsequence(S) 表示由字符串S的所有非空子序列构成的字符串集合。例如, S = "abc", 则Subsequence(S) = {"a", "b", "c", "ab", "ac", "bc", "abc"}
现在, 给你2个字符串S和T, 希望你能找到最小的k, 满足T ∈Subsequence(S ^ k)

Input

输入只有2行, 分别为字符串S和T (1 <= |S|, |T| <= 100,000), 输入保证字符串S和T只由小写字母构成。

Output

输出最小的k, 满足T ∈Subsequence(S ^ k), 若不存在这样的k, 则输出-1

Sample Input

abc
abacbc

Sample Output

3

Source
doraemon @ xmu
All Copyright Reserved 2007 Xiang Guangte, Zheng Le
Xmu.Csd All Rights Reserved.

/*以下解题报告来自同学 不是我搞的 
题目:字符串的运算
题目大意:
我们对字符串 S 做了以下定义:
1. S ^ k表示由k个字符串S构成的新字符串。 例如, S = "abc", k = 3, 则S ^ k  =  "abcabcabc"
2. Subsequence(S) 表示由字符串S的所有非空子序列构成的字符串集合。例如, S = "abc",
则Subsequence(S) = {"a", "b", "c", "ab", "ac", "bc", "abc"}
现在, 给你2个字符串S和T, 希望你能找到最小的k, 满足T ∈Subsequence(S ^ k)

解题思路:Subsequence(S)就是从S中选一些字符保持相对顺序不变组成的非空字符串;

  现在我们要把T分成K份这样的字符串,K越小越好,于是策略就是:先令k=0,在串S中找串T,从串T的第一个字符找起
  遇到了第一个字符再找第二个字符,一直找到不能找为止,然后k++,然后我们又返回到S串的开始位置继续找,直到把
  T中的所有字符都找完,最后的答案就是k,如果T中出现了S中没有的字符,那么这个字符是永远找不到的,最后答案就是-1;

  但是暴力枚举S串的下标时间复杂度是O(N^2),会超时,如果可以在S中找T的字符时快速定位,这样就不会超时了,因此
  可以先预处理一下S串,建立S串中每个字符的在S中出现的序列,比如字符'a'在S串中1,3,5位置出现过,那么字符'a'的序列
  就是(1,3,5)。有了这个序列后我们就可以快速定位一个我们想找的字符了,比如现在在S的i位置,我要找的下一个字符是
  'a',那么我们就用i+1下标在字符'a'的序列中二分找到最接近下标,如果没有我们就直接返回S串的开始位置,继续用上述的
  方法,这样时间复杂度就是降为O(N*log(N))。
*/


#include<stdio.h>
#include<string.h>
#include<set>
using namespace std;
int vis[30];
char s[110000],t[110000];
int find(int n,int m)
{
    int i,j=0,k=0,b;
    set <int> next[30];
    set <int> ::iterator it;
    memset(vis,0,sizeof(vis));
    for(i=0;i<n;i++)
        vis[s[i]-'a']=1,next[s[i]-'a'].insert(i);
    for(i=0;i<m;i++) if(!vis[t[i]-'a']) return -1;
    i=0;
    /*
    abc                    n=3
    abacbc                 m=6
    */
    while(j<m)
    {
        while(j<m)
        {
            b=t[j]-'a';
            it=next[b].lower_bound(i);//next里面存的是数组的下标
            /*函数lower_bound()在first和last中的前闭后开区间进行二分查找,
            返回大于或等于val的第一个元素位置。如果所有元素都小于val,则返回last的位置*/
            if(it==next[b].end())//没有找到比i大的下一个b的话  
            {
                if(i==0) return -1;//如果从第1个开始找都木有找到b字符那说明不存在 
                i=0; break;//没有找到比i大的下一个b 新增加一个s串从开头继续找
            }
            else i=(*it)+1,j++;//i前进到 *it对应的下标的下一个
        }
        k++;
    }
    return k;
}
int main()
{
    int i,j,n,m,flag;
    while(scanf("%s%s",s,t)!=EOF)
    {
        n=strlen(s);
        m=strlen(t);
        printf("%d\n",find(n,m));
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值