您的位置:首页 > 其它

POJ 3167 Cow Patterns

2016-09-26 13:09 309 查看
题目链接: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
,b
,n,m,k,p
,srt
,suma[S]
,sumb[S]
,an,ans
;

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,
4000
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  poj kmp