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
  • 相关阅读:
    元数据管理
    sqoop 安装
    postgres 索引
    postgres 表和库等信息大小统计
    Perl基础语法
    Perl 认识简介
    Oracle层次查询start with connect by
    jquery.cookie.js 的使用指南
    JavaScript中cookie使用
    CSS实现垂直居中的4种思路
  • 原文地址:https://www.cnblogs.com/geloutingyu/p/7346176.html
Copyright © 2011-2022 走看看