• 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
  • 相关阅读:
    JavaScript cookie详解
    Javascript数组的排序:sort()方法和reverse()方法
    javascript中write( ) 和 writeln( )的区别
    div做表格
    JS 盒模型 scrollLeft, scrollWidth, clientWidth, offsetWidth 详解
    Job for phpfpm.service failed because the control process exited with error code. See "systemctl status phpfpm.service" and "journalctl xe" for details.
    orm查询存在价格为空问题
    利用救援模式破解系统密码
    SSH服务拒绝了密码
    C# 调用 C++ DLL 中的委托,引发“对XXX::Invoke类型的已垃圾回收委托进行了回调”错误的解决办法
  • 原文地址:https://www.cnblogs.com/geloutingyu/p/7346176.html
Copyright © 2020-2023  润新知