HDU 4749 Parade Show 数据结构KMP变形

Parade Show

Every student has its own number, from 1 to n. (1<=n<=10^5), and they are standing from 1 to n in the increasing order the same with their number order. According to requirement of school authority, every queue is consisted of exactly m students. Because students who stand adjacent in training are assigned consecutive number, for better arrangement, you will choose in students with in consecutive numbers. When you choose these m students, you will rearrange their numbers from 1 to m, in the same order with their initial one.
  If we divide our students’ heights into k (1<=k<=25) level, experience says that there will exist an best viewing module, represented by an array a[]. a[i] (1<=i<=m)stands for the student’s height with number i. In fact, inside a queue, for every number pair i, j (1<=i,j<=m), if the relative bigger or smaller or equal to relationship between the height of student number i and the height of student number j is the same with that between a[i] and a[j], then the queue is well designed. Given n students’ height array x[] (1<=x[i]<=k), and the best viewing module array a[], how many well designed queues can we make at most?

 

Input

Multiple cases, end with EOF.
First line, 3 integers, n (1<=n<=10^5) m (1<=m<=n) k(1<=k<=25),
Second line, n students’ height array x[] (1<=x[i]<=k,1<=i<=n);
Third line, m integers, best viewing module array a[] (1<=a[i]<=k,1<=i<=m);
 

Output

One integer, the maximal amount of well designed queues.
 

Sample Input

    
    
10 5 10 2 4 2 4 2 4 2 4 2 4 1 2 1 2 1
 

Sample Output

  
  
1

解题思路:

输入的意思:n个学生,抽出连续的m个学生,他们的身高分为k个级别,然后就是n个身高,和最佳身高模式。

这题分析一下确实是kmp算法,但是有着很大的变形,首先因为是相对高度,不能直接的进行匹配,那怎么办呢

有一个思路:如果一个序列要和模式串匹配,那么对于每一个匹配的位置i他们前面的比他大的数字的数目比他小的数字的数目与他相等的数字的数目都要相等

(实际上如果比它小和相等的数字数目相等比它大也一定相等)满足这些条件就可以了,那么对应到kmp算法当中就是那个判断需要好好的修改一下

那么怎么判断?

1,kmp算法首先是通过自己和自己匹配找到的失配函数(失配函数就是next数组),这个匹配过程可以采用稍微暴力一点的做法

2,对于模式串,开一个数组g1[26][maxn],用来记录每一个位置他前面每一个数字有多少个。判断某一位置是否相等就可以用

这个g1数组

3,判断函数怎么写呢?(1)判断相等字符的数量是否相等(2)判断小于的字符数量是否相等,要写三个循环

4,仔细分析用同样的道理可以写出第二个匹配模式串的判断函数,

5,剩下的就是kmp的匹配了,每次匹配成功就记录一下,判断函数就是这一题的考点,写好之后基本就可以了


借鉴了网上的很多人的代码和思想,这题收获了很多,表示感谢。


#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 100005 ;
int g1[26][maxn] ;
int g2[26][maxn] ;
int p[maxn] ;
int a[maxn] ;
int b[maxn] ;
int vis[maxn] ;
int n,m,t ;

bool judge2(int x,int y){
    int c1 = 0;
    int c2 = 0;
    int c3 = 0;
    if(g1[a[x]][x-1]!=g1[a[y]][y-1]-g1[a[y]][y-x])return false;
    for(int i=1;i<a[x];i++)c1+=g1[i][x-1] ;
    for(int i=1;i<a[y];i++)c2+=g1[i][y-1] ;
    for(int i=1;i<a[y];i++)c3+=g1[i][y-x] ;
    if(c1!=c2-c3)return false;
    return true;
}
void getfail(){
    p[1] = 0 ;
    int j = 0 ;
    for(int i=2;i<=n;i++){
        while(j&&!judge2(j+1,i))j=p[j] ;
        if(judge2(j+1,i))j++ ;
        p[i] =j ;
    }
    return ;
}
bool judge(int x,int y){
    int c1=0;
    int c2=0;
    int c3=0;
    ///判断相等
    if(g1[a[x]][x-1]!=g2[b[y]][y-1]-g2[b[y]][y-x])return false;
    ///判断小于的
    for(int i=1;i<a[x];i++)c1+=g1[i][x-1] ;
    for(int i=1;i<b[y];i++)c2+=g2[i][y-1] ;
    for(int i=1;i<b[y];i++)c3+=g2[i][y-x] ;
    if(c1!=c2-c3)return false;
    return true ;
}
void kmp(){
    int j = 0 ;
    for(int i=1;i<=m;i++){
        while(j&&!judge(j+1,i))j=p[j] ;
        if(judge(j+1,i))j++ ;
        if(j==n){
            vis[i] = 1 ;
            //j = p[j] ;
            j = 0 ;
        }
    }
}

int main(){
    while(~scanf("%d%d%d",&m,&n,&t)){
        memset(g1,0,sizeof(g1));
        memset(g2,0,sizeof(g2));
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=m;i++){
            scanf("%d",&b[i]);
            for(int j=1;j<=t;j++)g2[j][i] = g2[j][i-1];
            g2[b[i]][i]++ ;
        }
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            for(int j=1;j<=t;j++)g1[j][i] = g1[j][i-1] ;
            g1[a[i]][i]++ ;
        }
        getfail() ;
        //printf("yes\n");
        kmp();
        //printf("yes\n");
        int ans = 0 ;
        int i = 1;
        while(i<=m){
            if(vis[i]){
                ans++ ;
                i+=n ;
            }else i++ ;
        }
        printf("%d\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值