zoukankan      html  css  js  c++  java
  • 四川省赛 SCU

    Censor

    frog is now a editor to censor so-called sensitive words (敏感词).

    She has a long text pp. Her job is relatively simple -- just to find the first occurence of sensitive word ww and remove it.

    frog repeats over and over again. Help her do the tedious work.

    Input

    The input consists of multiple tests. For each test:

    The first line contains 11 string ww. The second line contains 11 string pp.

    (1length of w,p51061≤length of w,p≤5⋅106, w,pw,p consists of only lowercase letter)

    Output

    For each test, write 11 string which denotes the censored text.

    Sample Input

        abc
        aaabcbc
        b
        bbb
        abc
        ab

    Sample Output

        a
        
        ab


    题意:给出两个字符串,在第二个里面去找寻有没有第一个,有的话删除,然后再合并字符串,问最后的字符串是怎样的
    如样例一 aaabcbc -> aabc -> a

    思路:我们想想,这么大的数据范围,光是我们查找子串是否存在就必须要用kmp,那我们再想想如何优化呢
    怎么解决那个删除之后再合并呢,我开始想的是用数组模拟链表,那样删除操作的话,只要改变一个指向就可以了
    列出下面我的错误代码
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    struct sss
    {
        int next;
        char c;
    }s1[5000001];
    int len1,len2;
    int head;
    int next1[5000001];
    char s2[5000001];
    char str[5000001];
    int num[5000001];
    void getnext(char s[])
    {
        int i=0,j=-1;
        next1[0]=-1;
        while(i<len2)
        {
            if(j==-1||s[i]==s[j])
            {
                next1[++i]=++j;
            }
            else j=next1[j];
        }
    }
    int kmp(struct sss s1[],char s2[])
    {
        int i=head,j=0;
        int cnt=0;
        num[cnt++]=i;
        while(i<len1)
        {
            if(j==-1||s1[i].c==s2[j])
            {
                i=s1[i].next;
                num[cnt++]=i;
                j++;
                if(j==len2) 
                {
                    if(cnt-len2-2<0) head=i; 
                    else s1[num[cnt-len2-2]].next=i;
                    return 1;
                }
            }
            else  j=next1[j];
        }
        return 0;
    }
    int main()
    {
        while(scanf("%s",s2)!=EOF)
        {
            scanf("%s",str);
            len1=strlen(str);
            len2=strlen(s2); 
            if(len2>len1)
            {
                printf("%s
    ",str);
                continue;
            } 
            for(int i=0;i<len1;i++)
            {
                s1[i].next=i+1;
                s1[i].c=str[i];
            }
            getnext(s2);
            head=0;
            while(kmp(s1,s2));
            int i=head;
            while(i<len1)
            {
                printf("%c",s1[i].c);
                i=s1[i].next;
            }
            printf("
    ");
        }
    }
    View Code

    但是我的想法是一旦删除之后,就改变指向,然后再次退出去,然后再从头开始找,这样无疑还是超时了

    我们再想想怎么利用kmp的性质去优化,我在每个位置存下模式串当前匹配到哪个位置,

    然后我用一个数组存下来,当`我找到一个之后,那个匹配之前的位置的模式位置值又赋值给当前即可

    如    样例一

    aaabcbc

    01112323

    abc

    删除一遍后j的值并不是从0开始而是赋值abc之前的那个位置值1,代表以后匹配了a,无需再去比

    字符串中我们需要灵活的利用kmp的思想来优化时间复杂度

    具体看代码

    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<algorithm>
    #include<queue>
    using namespace std;
    typedef long long LL ;
    const int MX=5555555;
    char s[MX],t[MX],ans[MX];
    int  next1[MX],pos[MX],len1,len2;
     
    void init() {
        memset(ans,0,sizeof(ans));
        memset(pos,0,sizeof(pos));
        memset(next1,0,sizeof(next1));
    }
     
    struct Node {
        char ch;
        int j;
        Node() {};
        Node(char c,int n):ch(c),j(n) {};
    };
     
    void GetNext() {
        int i=0,j=-1;
        next1[0]=-1;
        while(i<len2) {
            if(j==-1||t[i]==t[j]) {
                i++;
                j++;
                if(t[i]==t[j]) {
                    next1[i]=next1[j];
                } else next1[i]=j;
            } else j=next1[j];
        }
    }
    void KMP() {
        int i=0,j=0;
        int cnt=0;
        while(i<len1) {
            ans[cnt]=s[i++]; 
            while(!(j==-1||ans[cnt]==t[j])) {
                j=next1[j];
            }
            j++;
            cnt++; 
            pos[cnt]=j;//存当前模式串匹配到哪个位置
            if(j==len2) {
                cnt-=len2;//直接赋值之前的位置给他
                j=pos[cnt];
            }
        }
        for(int i=0; i<cnt; i++) {
            putchar(ans[i]);
        }
        puts("");
    }
    int main() {
        while(~scanf("%s %s",t,s)) {
            init();
            len1=strlen(s);
            len2=strlen(t);
            GetNext();
            KMP(); 
        }
        return 0;
    }
  • 相关阅读:
    C 语言 字符串命令 strstr()的用法 实现将原字符串以分割串分割输出
    C# 中对 IEnumerable IEnumerator yield 的理解
    C 语言 用字符输出菱形图案的函数(可自定义边长及字符样式)
    C 语言 对角线添充二维数组
    C 语言 边读 边写入文件
    [转]Page.RegisterRequiresRaiseEvent()与Page.RegisterRequiresPostBack()
    asp.net 判断是手机或电脑访问网页的方式
    表达式树【转】
    ADO.NET中的“返回多个结果集”和“MARS”【转】
    SQL2005转2000的方法【转】
  • 原文地址:https://www.cnblogs.com/Lis-/p/9560106.html
Copyright © 2011-2022 走看看