[kmp] POJ3167 Cow Patterns

Cow Patterns
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 3849 Accepted: 1378
Description

A particular subgroup of K (1 <= K <= 25,000) of Farmer John’s cows likes to make trouble. When placed in a line, these troublemakers stand together in a particular order. In order to locate these troublemakers, FJ has lined up his N (1 <= N <= 100,000) cows. The cows will file past FJ into the barn, staying in order. FJ needs your help to locate suspicious blocks of K cows within this line that might potentially be the troublemaking cows.

FJ distinguishes his cows by the number of spots 1..S on each cow’s coat (1 <= S <= 25). While not a perfect method, it serves his purposes. FJ does not remember the exact number of spots on each cow in the subgroup of troublemakers. He can, however, remember which cows in the group have the same number of spots, and which of any pair of cows has more spots (if the spot counts differ). He describes such a pattern with a sequence of K ranks in the range 1..S. For example, consider this sequence:

  1 4 4 3 2 1

In this example, FJ is seeking a consecutive sequence of 6 cows from among his N cows in a line. Cows #1 and #6 in this sequence have the same number of spots (although this number is not necessarily 1) and they have the smallest number of spots of cows #1..#6 (since they are labeled as ‘1’). Cow #5 has the second-smallest number of spots, different from all the other cows #1..#6. Cows #2 and #3 have the same number of spots, and this number is the largest of all cows #1..#6.

If the true count of spots for some sequence of cows is:

 5 6 2 10 10 7 3 2 9

then only the subsequence 2 10 10 7 3 2 matches FJ’s pattern above.

Please help FJ locate all the length-K subsequences in his line of cows that match his specified pattern.
Input

Line 1: Three space-separated integers: N, K, and S

Lines 2..N+1: Line i+1 describes the number of spots on cow i.

Lines N+2..N+K+1: Line i+N+1 describes pattern-rank slot i.
Output

Line 1: The number of indices, B, at which the pattern matches

Lines 2..B+1: An index (in the range 1..N) of the starting location where the pattern matches.
Sample Input

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

1
3
Hint

Explanation of the sample:

The sample input corresponds to the example given in the problem statement.

There is only one match, at position 3 within FJ’s sequence of N cows.

模式串可以浮动的模式匹配问题。
例如,给定主串5,6,2,10,10,7,3,2,9和模式串1,4,4,3,2,1,其中模式串表示了各个数字之间的相对大小,需要找出模式串的所有匹配位置。

换句话说,就是找到所有的位置,使主串和模式串之间的相对大小保持一致。顺着这个思路,会想到怎样把主串和模式串的相对大小全部找出来,但因为在不同的区间内,二者的相对大小会发生变化,所以不可能先做预处理。

那怎么办呢?怎样使主串和模式串之间的相对大小保持一致?可以考虑小于它的元素个数,以及等于它的元素个数:
在1,4,4,3,2,1中,
分别小于每个的元素个数为0,4,4,3,2,0;
分别等于他自身的元素个数2,2,2,1,1,2;
在2,10,10,7,3,2中
分别小于每个的元素个数为0,4,4,3,2,0;
分别等于他自身的元素个数2,2,2,1,1,2;
完全相同。

因此推出结论:
两个序列的偏序关系相同<==>这两个序列的每个位置上的元素前面等于它的元素个数和小于它的元素个数都相等。
所以只要统计小于它的元素个数,还有和当前数相等的元素就可以进行匹配。
然后进行kmp即可。

#include <iostream>
#include <map>
#include <cstdio>
#include<algorithm>
#include <cstring>
#include <cmath>
#include<vector>
#include <set>
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; //as[0][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[MAXM];
void kmp_pre()
{
    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++)
        {
            if(i-j>0) t11 += bs[i][k] - bs[i-j-1][k];
            else t11 += bs[i][k];
        }
        if(i-j>0) 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 += bs[j][k];
        }
        t22 = bs[j][b[j]];
        if(j == -1 || (t11 == t21 && t12 == t22))
        {
            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)
    {
        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>0) 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();
        kmp();
        printf("%d\n",ans.size());
        for(int i = 0 ; i< ans.size();i++)
            printf("%d\n",ans[i]);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值