POJ 3167 Cow Patterns

题目链接:http://poj.org/problem?id=3167

Description

约翰的N(1≤N≤100000)只奶牛中出现了K(1≤K≤25000)只爱惹麻烦的坏蛋。奶牛们按一定的顺序排队的时候,这些坏蛋总会站在一起。为了找出这些坏蛋,约翰让他的奶牛排好队进入牛棚,同时需要你的慧眼来识别坏蛋,为了区分,约翰给所有奶牛都发了号牌,上面写着一个1~S(1≤S≤25)之间的数字。虽然这不是一个完美的方法,但也能起一点作用。现在,约翰已经不记得坏蛋们的具体号码。但是凭他的记忆,他给出一个“模式串”。原坏蛋的号码如果相同,模式串中他们的号码依然相同。模式串中坏蛋们之间号码的大小关系也与原号码相同的。比如,对于这样一个模式串:1,4,4,3,2,1。原来的6只坏蛋,排最前面的与排最后的号码相同(尽管不一定是1),而且他们的号码在团伙中是最小的。第2,3位置的坏蛋,他们的号码也相同(不一定是4),且是坏蛋团伙中最大的。现在所有奶牛排成队列,号码依次是这样:5,6,2,10,10,7,3,2,9存在子串2,10,10,7,3,2,满足模式串的相同关系和大小关系,所以这就是坏蛋团伙,请找出K个坏蛋的困伙的所有可能性。

Input

第1行输入三个整数N,K,S。接下来N行每行输入一只牛的号码。接下来K行每行输入一个模式串的号码。

Output

第1行输出一个整数B。接下来B行,每行一个整数,表示一种可能下的坏蛋团伙的起始位置。

Sample Input

9 6 10
5
6
2
10
10
7
3
2
9
1
4
4
3
2
1

Sample Output

1
3
[翻译来自BZOJ 1729]


Solution

这道题如果没有那种奇怪的匹配方式的话,就是一道很果的kmp。但这题的匹配方式怎么看怎么恶心
先把文本串取名为a串,模式串叫b串。以下所说的”相等”若无特殊说明即为题意所说的相等
定义一个函数getval(ch,str,l,r),返回的值为ch在str[l~r]中的真实值,即将str[l~r]中的值排序离散化后ch的值。则题意即为:在a串中找出一段长为m的区间[l,r],使对任意的1≤i≤m,getval(a[l+i-1],a,l,r)均等于getval(b[i],b,1,m)
可以发现这样一个特性:
这里写图片描述
当a串即将匹配到i,b串已经匹配到j时,即两端蓝色相等时,只要getval(a[i],a,l,r)=getval(b[j+1],b,1,j+1),两段蓝色+黄色也相等
可以,这貌似跟普通的字符串匹配长得一模一样,这很kmp。有了这个特性,只要能快速求出getval()的值,就成了一个普通的kmp了。
getval的值,其实是返回某个数(数跟字符其实是一样的)在某堆数中的排名。是什么决定了一个数x的排名?是小于x的数的个数,等于x的数的个数。如果这两个个数与另一个y的两个个数都相等,那么x跟y在各自字符串的各自区间内的getval值就相等了
那么就可以O(n*s)求出某个字符ch在1~i的出现次数,用前缀和的方式求出来,然后就能O(1)求出某个区间某个字符出现次数。至于小于某个字符的出现次数,当场大力O(s)求一下就好啦
总时间复杂度O(n*s)


Code

这里写图片描述

#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
#define N 100010
#define S 26
#define mid ((l+r)>>1)
#define foru(i,l,r) for (int i=l; i<=r; i++)
#define read(x) (x=getint())

int a[N],b[N],n,m,k,p[N],srt[N],suma[S][N],sumb[S][N],an,ans[N];

int getint()
{
    int x,f=1; char ch;
    while (!isdigit(ch=getchar())) f=ch=='-'?-1:1;
    x=ch-'0';
    while (isdigit(ch=getchar())) x=x*10+ch-'0';
    return f*x;
}
void init()
{
    read(n), read(m), read(k);
    foru(i,1,n) read(a[i]);
    foru(i,1,m) read(b[i]);
    foru(i,1,n)
        foru(j,1,k)
            suma[j][i]+=suma[j][i-1]+(j==a[i]);
    foru(i,1,m)
        foru(j,1,k)
            sumb[j][i]+=sumb[j][i-1]+(j==b[i]);
}
int geta(int ch, int l, int r)
{
    int ans=0;
    foru(i,1,ch-1) ans+=suma[i][r]-suma[i][l-1];
    return ans;
}
int getb(int ch, int l, int r)
{
    int ans=0;
    foru(i,1,ch-1) ans+=sumb[i][r]-sumb[i][l-1];
    return ans;
}
bool cmpab(int x, int y)
{
    if (getb(b[x],1,x)!=geta(a[y],y-x+1,y)) return false;
    return sumb[b[x]][x]==suma[a[y]][y]-suma[a[y]][y-x];
}
bool cmpbb(int x, int y)
{
    if (getb(b[x],1,x)!=getb(b[y],y-x+1,y)) return false;
    return sumb[b[x]][x]==sumb[b[y]][y]-sumb[b[y]][y-x];
}
void init_kmp(int s[], int n)
{
    p[1]=0;
    int j=0;
    foru(i,2,n)
    {
        while (j&&!cmpbb(j+1,i)) j=p[j];
        if (cmpbb(j+1,i)) j++;
        p[i]=j;
    }   
}
void kmp()
{
    int j=0;
    foru(i,1,n)
    {
        while (j&&!cmpab(j+1,i)) j=p[j];
        if (cmpab(j+1,i)) j++;
        if (j==m)
        {
            ans[++an]=i-j+1;
            j=p[j];
        }
    }
}
int main()
{ 
//  freopen("data.txt","r",stdin);
    init(); 
    init_kmp(b,m);
    kmp();
    printf("%d\n",an);
    foru(i,1,an) printf("%d\n",ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值