人生如此复杂,机会多得像稠密图,我们没理由认输。尽管我们走不了最短路,但图仍是连通图。TLE之前,没有一个节点叫失败。(You know what's cooking? BOOM~~)!

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支持

posted @ 2019-08-14 17:29  CXYscxy  阅读(236)  评论(1编辑  收藏  举报
Live2D