• KMP---基因改造


    基因改造

    http://oi.nks.edu.cn/zh/Problem/Details/5942

    问题描述

    "人类智慧的冰峰,只有萌萌哒的我寂寞地守望。"
    --TB
    TB正走在改造人类智慧基因的路上。TB发现人类智慧基因一点也不萌萌哒,导致人类普遍智商过低,为了拯救低智商人群,博爱的TB开始改造人类智慧基因。人类智慧DNA由C种人类智慧脱氧核苷酸构成(这是一种十分特殊的DNA)。

    TB智慧DNA片段T长度为M,她可以把另一段长度为M的人类智慧DNA片段S改造成T。

    改造过程中,TB可以充分发挥智慧,将任意两种人类智慧脱氧核苷酸交换(比如对于片段S=12321,交换1和2变成S=21312,交换1和4变成42324),可以无限次交换。如果S可以通过若干次交换变成T,那么就称S为"萌萌哒人类基因片段"。

    TB想知道对于一个长度为N的人类智慧DNA片段S[1 ~ N],有多少个长度为M的连续子片段S[i ~ i+M-1],是"萌萌哒人类基因片段",并且这些"萌萌哒人类基因片段"在哪里。

    输入格式

    第一行包含两个正整数case和C,分别表示数据组数和人类智慧脱氧核苷酸的种数。

    接下来3*case行,每三行表示一组数据:
    第一行一个正整数N和M,表示人类智慧DNA片段S和TB智慧DNA片段T的长度。
    第二行N个正整数,表示人类智慧DNA片段S。
    第三行M个正整数,表示TB智慧DNA片段T。

    对于所有数据数据,case=3, n,m,C<=100000

    输出格式

    对于每组数据:
    第一行一个正整数tot,表示"萌萌哒人类基因片段"的个数。
    接下来一行tot个用空格隔开的正整数pos,表示"萌萌哒人类基因片段"开头所在的位置。要求从小到大输出每个pos。

    样例输入

    3 3
    6 3
    1 2 1 2 3 2
    3 1 3
    6 3
    1 2 1 2 1 2
    3 1 3
    6 3
    1 1 2 1 2 1
    3 1 3

    样例输出

    3
    1 2 4
    4
    1 2 3 4
    3
    2 3 4

    提示

    对于第一组数据:
    S[ 1 ~ 3]=121,可以先将1和2交换变成212,再将2和3交换变成313。
    S[2 ~ 4]=212,可以将2和3交换变成313。
    S[4 ~ 6]=232,可以先将2和3交换变成323,再将1和2交换变成313。


    来源  bzoj 4641
     

    分析

    题目要求在长度为 n 的串 S 中找出长度为 m 的符合要求的子串, 并输出所有位
    置, 由此可以想到 KMP
    题目允许将子串中任意两种数字交换, 这意味着子串中所有的数字都可以改
    变, 不变的只有同种数字之间的位置关系。 例如 313211312323213 之间
    可以互相转换, 在 KMP 时这几个串应该被当作相同的串。 因此用来 KMP 的两
    个串必须体现这种位置关系。
    对原串进行修改, 把第 i 个位置的数字修改为这个数字上一次出现的位置与当
    前位置之差, 若之前没有出现过, 修改为 0。 例如 313211312323213
    个串经修改后都会变为 00203
    但这样修改后也不能够直接 KMP, 因为有 0 的位置并不能完全体现同种数字的
    位置关系。 因此对于这些位置要分情况讨论(设当前与 T 匹配的 S 的子串为
    S') :
    • S'T 完全相同, 例如 S'=010T=010。 这时两个串可以直接被判定为相
    同。
    • T 的某个位置不为 0, 而 S'的对应位置为 0, 例如 S'=010T=011。 则 T
    这个位置一定是一个之前出现过的数字, S'的这个位置一定是一个之前没
    有出现过的数字, 两个串肯定不同。
    • T 的某个位置为 0, 而 S'的对应位置不为 0, 这里给两个例子:
    • S'=012T=002S'中第二个位置的数字应该与第一个位置相同, 但 T 的前
    两个位置的数字都是第一次出现, 肯定不相同, 因此这两个串不相同。
    • S'=022T=002S'中第二个位置的数字应该与在它前面距离为 2 的位置相
    同, 而这个位置不在子串 S'内, 所以这个数字仍被当作第一次出现, 因此
    这两个串相同。
    因此 KMP 匹配时需要重新定义比较法则, 具体实现见代码。
    题目中的 C 没有用处, 各位神犇不要多虑。

    代码

    #include<stdio.h>
    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=100000+5;
    int a[maxn],b[maxn],mark[maxn],ans,pos[maxn],Next[maxn];
    bool kmp(int l,int r)
    {
        int j=0;
        for (int i=l; i<=r; i++)
        {
            int x=a[i];
            if (i-a[i]<l) x=0;
            while (j>0&&b[j+1]!=x) j=Next[j];
            if (x==b[j+1]) j++;
            if (j==r-l+1)
                return true;
        }
        return false;
    }
    int main()
    {
        ios::sync_with_stdio(false);
        cout.tie(NULL);
        int n,m;
        cin>>n>>m;
        for (int i=1; i<=n; i++)
        {
            int y,z;
            cin>>y>>z;
            for (int j=1; j<=y; j++) //人类智慧DNA片段S
            {
                int x;
                cin>>x;
                if (mark[x])//x出现过
                    a[j]=j-mark[x];//计算大小
                else a[j]=0;//未出现过,赋值为0
                mark[x]=j;//标记已出现,储存位置
            }
            memset(mark,0,sizeof(mark));//清空,重复使用
            int r=0;
            Next[1]=Next[0]=0;
            for (int j=1; j<=z; j++) //TB智慧DNA片段T
            {
                int x;
                cin>>x;
                if (mark[x])
                    b[j]=j-mark[x];//计算大小
                else b[j]=0;
                mark[x]=j;
                if (j>1)//Next的预处理
                {
                    while (r>0&&b[r+1]!=b[j]) r=Next[r];//当b[r+1]和b[j]不匹配时,减小r
                    if (b[r+1]==b[j]) r++;
                    Next[j]=r;
                }
            }
            memset(mark,0,sizeof(mark));
            r=0;
            for (int j=1; j<=y; j++)//KMP 
            {
                int x=a[j];
                if (j-a[j]<j-r) x=0;
                while (r&&b[r+1]!=x)
                {
                    r=Next[r];
                    if (j-a[j]<j-r) x=0;
                }
                if (b[r+1]==x) r++;
                if (r==z)
                    ans++,pos[ans]=j-r+1,r--;
            }
            cout<<ans<<endl;
            for (int j=1; j<=ans; j++)
                cout<<pos[j]<<" ",pos[j]=0;
            cout<<endl;
            memset(a,0,sizeof(a));
            memset(b,0,sizeof(b));
            ans=0;
        }
    }

     感谢baijiu支持

  • 相关阅读:
    第一次作业-准备篇
    个人作业——软件工程实践总结&个人技术博客
    个人技术总结-spring boot编写接口和数据返回
    个人作业——软件评测
    结对第二次作业——某次疫情统计可视化的实现
    结对第一次—疫情统计可视化(原型设计)
    软工实践寒假作业(2/2)
    软工实践寒假作业(1/2)
    个人作业-软件工程实践总结
    Android实现多图选择
  • 原文地址:https://www.cnblogs.com/CXYscxy/p/11353448.html
Copyright © 2020-2023  润新知