zoukankan      html  css  js  c++  java
  • poj3167(kmp)

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

    题意: 给出两串数字 s1, s2, 求主串 s1 中的 s2 匹配数并输出每个匹配的开头位置. 区间 [l, r] 是 s2 的一个匹配当且仅当 s1[i] 是 [l, r] 中的第 s2[i - l] 大元素, 其中 l <= i <= r.

    思路: 首先有个结论, 两个串的排名串相等当且仅当这两个串的每个位置上的元素前面等于它的元素个数和小于它的元素个数都相等. 那么可以先花 s * n + s * m 的时间预处理一下 vis1[i][j] 为 s1 前 i 个数字中有多少个小于等于 j, vis2[i][j] 为 s2 前 i 个数字中有多少个小于等于j. 然后可以用 kmp 匹配即可. 注意一下和一般 kmp 不同的是, 在一般 kmp 中的 s2[i] == s2[j] 和 s1[i] == s2[j] 条件要换成区间 (i - j, i] 中小于等于 s2[i] 的数和区间 [1, j] 中小于等于 s2[j] 的数都相等以及区间 (i - j, i] 中小于等于 s1[i] 的数和区间 [1, j] 中小于等于 s2[j] 的数都相等. 这是由前面那个结论决定的.

    代码:

     1 #include <iostream>
     2 #include <stdio.h>
     3 #include <string.h>
     4 using namespace std;
     5 
     6 const int MAXN = 1e5 + 10;
     7 int n, m, s;
     8 int s1[MAXN], s2[MAXN], nxt[MAXN];
     9 int vis1[MAXN][30], vis2[MAXN][30], sol[MAXN], tot;
    10 //vis1[i][j]为s1前i个数字中有多少个小于等于j
    11 //vis2[i][j]为s2前i个数字中有多少个小于等于j
    12 
    13 void init(void){
    14     memset(nxt, 0, sizeof(nxt));
    15     memset(vis1, 0, sizeof(vis1));
    16     memset(vis2, 0, sizeof(vis2));
    17     tot = 0;
    18 }
    19 
    20 void gel(void){
    21     for(int i = 1; i <= n; i++){
    22         for(int j = 1; j <= s; j++){
    23             vis1[i][j] = vis1[i - 1][j];
    24         }
    25         vis1[i][s1[i]]++;
    26     }
    27     for(int i = 1; i <= m; i++){
    28         for(int j = 1; j <= s; j++){
    29             vis2[i][j] = vis2[i - 1][j];
    30         }
    31         vis2[i][s2[i]]++;
    32     }
    33 }
    34 
    35 void get_nxt(void){
    36     int i = 1, j = 0;
    37     while(i <= m){
    38         int cnt1 = 0, cnt2 = 0, cc1 = 0, cc2 = 0;
    39         for(int k = 1; k < s2[i]; k++){
    40             cnt1 += vis2[i][k] - vis2[i - j][k]; //累计s2区间(i-j,i]中小于s2[i]的数
    41         }
    42         cc1 = vis2[i][s2[i]] - vis2[i - j][s2[i]]; //s2区间(i-j,i]中等于s2[i]的数
    43         for(int k = 1; k < s2[j]; k++){
    44             cnt2 += vis2[j][k]; //累计s2区间[1,j]中小于s2[j]的数
    45         }
    46         cc2 = vis2[j][s2[j]]; //s2区间[1,j]中等于s2[j]的数
    47         if(j == 0 || (cnt1 == cnt2 && cc1 == cc2)) nxt[++i] = ++j;
    48         else j = nxt[j];
    49     }
    50 }
    51 
    52 
    53 void kmp(void){
    54     int i = 1, j = 1;
    55     while(i <= n){
    56         int cnt1 = 0, cnt2 = 0, cc1 = 0, cc2 = 0;
    57         for(int k = 1; k < s1[i]; k++){ //累计s1区间(i-j,i]中小于s1[i]的数
    58             cnt1 += vis1[i][k] - vis1[i - j][k];
    59         }
    60         cc1 = vis1[i][s1[i]] - vis1[i - j][s1[i]]; //s1区间(i-j,i]中等于s1[i]的数
    61         for(int k = 1; k < s2[j]; k++){
    62             cnt2 += vis2[j][k];//累计s2区间[1,j]中小于s2[j]的数
    63         }
    64         cc2 = vis2[j][s2[j]];//s2区间[1,j]中等于s2[j]的数
    65         if(j == 0 || (cnt1 == cnt2 && cc1 == cc2)){
    66             i++;
    67             j++;
    68         }else j = nxt[j];
    69         if(j == m + 1){
    70             sol[tot++] = i - j + 1;
    71             j = nxt[j];
    72         }
    73     }
    74 }
    75 
    76 int  main(void){
    77     while(~scanf("%d%d%d", &n, &m, &s)){
    78         for(int i = 1; i <= n; i++){
    79             scanf("%d", &s1[i]);
    80         }
    81         for(int i = 1; i <= m; i++){
    82             scanf("%d", &s2[i]);
    83         }
    84         init();
    85         gel();
    86         get_nxt();
    87         kmp();
    88         printf("%d
    ", tot);
    89         for(int i = 0; i < tot; i++){
    90             printf("%d
    ", sol[i]);
    91         }
    92     }
    93     return 0;
    94 }
    View Code
  • 相关阅读:
    [转]《鸟人》想拍个文艺片,为什么要自虐
    [转] Java内部类之闭包(closure)与回调(callback)
    编程模型的笔记
    delphi char数组、string和Pchar的相互转换
    Char 与 Byte
    根据函数名称---函数指针--调用函数
    procedure of object 对象的函数指针
    虚方法、抽象方法、抽象类、重定义、覆盖重写------我自己
    Delphi 中的自动释放策略
    Delphi中设置条件断点
  • 原文地址:https://www.cnblogs.com/geloutingyu/p/7346176.html
Copyright © 2011-2022 走看看