POJ3167

KMP上跑浮动的模式串

一个农场主有N头牛,每个牛都以它身上的斑点数作为标志,以斑点数作为标准对牛进行rank排序,定义牛的序列的相同性为第i头牛及其之前的牛的比他rank小的数量和与他rank相同的数量,比如:

1 4 4 3 2 1 和 2 10 10 7 3 2 是一样的模式串

要求数有多少个符合要求的模式串,并输出每个符合要求的子串的起始位置。

 

KMP!!!太强啦,将这个浮动的串转换一下,只要能正确找出两个串等价的条件,而且在该条件下满足后面新加进来的元素不会影响前面匹配的合法性,就可以用KMP解决,虽然代码长了一点……此处为kuangbin的模板,加了一点注释的理解

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int MAXN = 100010;
const int MAXM = 25010;
int a[MAXN];
int b[MAXM];
int n,m,s;
int as[MAXN][30];
int bs[MAXM][30];
void init()
{
    for(int i=0;i<n;i++)
    {
        if(i==0)
        {
            for(int j=1;j<=25;j++)
                as[i][j]=0;
        }
        else
        {
            for(int j=1;j<=25;j++)
                as[i][j]=as[i-1][j];
        }
        as[i][a[i]]++;
    }
    for(int i=0;i<m;i++)
    {
        if(i==0)
        {
            for(int j=1;j<=25;j++)
                bs[i][j]=0;
        }
        else
        {
            for(int j=1;j<=25;j++)
                bs[i][j]=bs[i-1][j];
        }
        bs[i][b[i]]++;
    }
}
int Next[MAXN];
void kmp_pre()              //处理出Next数组
{
    int i,j;
    j=Next[0]=-1;
    i=0;
    while(i<m)
    {
        int t11=0,t12=0,t21=0,t22=0;
        for(int k=1;k<b[i];k++)             // t11  从i往前j个牛有多少个比bi小的
        {
            if(i-j>0)
                t11+=bs[i][k]-bs[i-j-1][k];
            else
                t11+=bs[i][k];
        }
        if(i-j>0)                           // t22  从i往前j个牛,有多少个和bi一样的
            t12=bs[i][b[i]]-bs[i-j-1][b[i]];
        else
            t12=bs[i][b[i]];
        for(int k=1;k<b[j];k++)             //t21   第j头牛之前有多少个比bj小的
        {
            t21+=bs[j][k];
        }
        t22=bs[j][b[j]];                    //t22   第j头牛之前有多少个和bj一样的
        if(j==-1 ||  (t11==t21 && t12==t22))    //转化为正常kmp思路
        {
            Next[++i]=++j;
        }
        else
            j=Next[j];
    }
}

vector<int> ans;
void kmp()
{
    ans.clear();
    int i,j;
    kmp_pre();
    i=j=0;
    while(i<n)
    {
        //和kmppre一样的处理方法,计算小于本rank的和等于本rank的
        int t11=0,t12=0,t21=0,t22=0;
        for(int k=1;k<a[i];k++)
        {
            if(i-j>0)
                t11+=as[i][k]-as[i-j-1][k];
            else
                t11+=as[i][k];
        }
        if(i>j)
            t12=as[i][a[i]]-as[i-j-1][a[i]];
        else
            t12=as[i][a[i]];
        for(int k=1;k<b[j];k++)
        {
            t21+=bs[j][k];
        }
        t22=bs[j][b[j]];
        if(j==-1 || (t11==t21 && t12==t22))
        {
            i++;
            j++;
            if(j>=m)
            {
                ans.push_back(i-m+1);
                j=Next[j];
            }
        }
        else
            j=Next[j];
    }
}

int main()
{
    while(scanf("%d%d%d",&n,&m,&s)==3)
    {
        for(int i=0;i<n;i++)
            scanf("%d",&a[i]);
        for(int i=0;i<m;i++)
            scanf("%d",&b[i]);
        init();//处理出每个ai和它前面有多少个rank为j的,bi也处理出来
        kmp();
        printf("%d\n",ans.size());
        for(int i=0;i<ans.size();i++)
            printf("%d\n",ans[i]);
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值