题目链接: 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] 的数都相等. 这是由前面那个结论决定的.

代码:

 #include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std; const int MAXN = 1e5 + ;
int n, m, s;
int s1[MAXN], s2[MAXN], nxt[MAXN];
int vis1[MAXN][], vis2[MAXN][], sol[MAXN], tot;
//vis1[i][j]为s1前i个数字中有多少个小于等于j
//vis2[i][j]为s2前i个数字中有多少个小于等于j void init(void){
memset(nxt, , sizeof(nxt));
memset(vis1, , sizeof(vis1));
memset(vis2, , sizeof(vis2));
tot = ;
} void gel(void){
for(int i = ; i <= n; i++){
for(int j = ; j <= s; j++){
vis1[i][j] = vis1[i - ][j];
}
vis1[i][s1[i]]++;
}
for(int i = ; i <= m; i++){
for(int j = ; j <= s; j++){
vis2[i][j] = vis2[i - ][j];
}
vis2[i][s2[i]]++;
}
} void get_nxt(void){
int i = , j = ;
while(i <= m){
int cnt1 = , cnt2 = , cc1 = , cc2 = ;
for(int k = ; k < s2[i]; k++){
cnt1 += vis2[i][k] - vis2[i - j][k]; //累计s2区间(i-j,i]中小于s2[i]的数
}
cc1 = vis2[i][s2[i]] - vis2[i - j][s2[i]]; //s2区间(i-j,i]中等于s2[i]的数
for(int k = ; k < s2[j]; k++){
cnt2 += vis2[j][k]; //累计s2区间[1,j]中小于s2[j]的数
}
cc2 = vis2[j][s2[j]]; //s2区间[1,j]中等于s2[j]的数
if(j == || (cnt1 == cnt2 && cc1 == cc2)) nxt[++i] = ++j;
else j = nxt[j];
}
} void kmp(void){
int i = , j = ;
while(i <= n){
int cnt1 = , cnt2 = , cc1 = , cc2 = ;
for(int k = ; k < s1[i]; k++){ //累计s1区间(i-j,i]中小于s1[i]的数
cnt1 += vis1[i][k] - vis1[i - j][k];
}
cc1 = vis1[i][s1[i]] - vis1[i - j][s1[i]]; //s1区间(i-j,i]中等于s1[i]的数
for(int k = ; k < s2[j]; k++){
cnt2 += vis2[j][k];//累计s2区间[1,j]中小于s2[j]的数
}
cc2 = vis2[j][s2[j]];//s2区间[1,j]中等于s2[j]的数
if(j == || (cnt1 == cnt2 && cc1 == cc2)){
i++;
j++;
}else j = nxt[j];
if(j == m + ){
sol[tot++] = i - j + ;
j = nxt[j];
}
}
} int main(void){
while(~scanf("%d%d%d", &n, &m, &s)){
for(int i = ; i <= n; i++){
scanf("%d", &s1[i]);
}
for(int i = ; i <= m; i++){
scanf("%d", &s2[i]);
}
init();
gel();
get_nxt();
kmp();
printf("%d\n", tot);
for(int i = ; i < tot; i++){
printf("%d\n", sol[i]);
}
}
return ;
}

poj3167(kmp)的更多相关文章

  1. 【POJ 3167】Cow Patterns (KMP+树状数组)

    Cow Patterns Description A particular subgroup of K (1 <= K <= 25,000) of Farmer John's cows l ...

  2. KMP算法求解

    // KMP.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include<iostream> using namespac ...

  3. 简单有效的kmp算法

    以前看过kmp算法,当时接触后总感觉好深奥啊,抱着数据结构的数啃了一中午,最终才大致看懂,后来提起kmp也只剩下“奥,它是做模式匹配的”这点干货.最近有空,翻出来算法导论看看,原来就是这么简单(先不说 ...

  4. KMP算法

    KMP算法是字符串模式匹配当中最经典的算法,原来大二学数据结构的有讲,但是当时只是记住了原理,但不知道代码实现,今天终于是完成了KMP的代码实现.原理KMP的原理其实很简单,给定一个字符串和一个模式串 ...

  5. 萌新笔记——用KMP算法与Trie字典树实现屏蔽敏感词(UTF-8编码)

    前几天写好了字典,又刚好重温了KMP算法,恰逢遇到朋友吐槽最近被和谐的词越来越多了,于是突发奇想,想要自己实现一下敏感词屏蔽. 基本敏感词的屏蔽说起来很简单,只要把字符串中的敏感词替换成"* ...

  6. [KMP]【学习笔记】

    Oulipo Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 36916   Accepted: 14904 Descript ...

  7. KMP算法实现

    链接:http://blog.csdn.net/joylnwang/article/details/6778316 KMP算法是一种很经典的字符串匹配算法,链接中的讲解已经是很明确得了,自己按照其讲解 ...

  8. KMP专题

    1.[HDU 3336]Count the string(KMP+dp) 题意:求给定字符串含前缀的数量,如输入字符串abab,前缀是a.ab.aba.abab,在原字符串中出现的次数分别是2.2.1 ...

  9. KMP学习之旅

    说起kmp就要从字符串的匹配说起,下面我们谈谈字符串的匹配 给定一个原字符串:bababababababababb,再给定一个模式串:bababb,求模式串是否在源字符串中出现 最简单的方法就是遍历源 ...

随机推荐

  1. (转)更改Web.config中对上传文件大小限制

    .net上传超过200K的图片的时候,会跳转到404,但是url没有错误,真J8的坑啊. 本文转载自:http://www.cnblogs.com/zwffff/archive/2009/04/29/ ...

  2. linux指令 apt-grt指令使用

    apt-get 是linux的一条指令,主流的linux版本Debian和ubuntu都使用apt-get来安装软件.那么,需安装的软件都放在哪里呢??? apt-get 利用软件安装源来安装软件,其 ...

  3. 【转】 Pro Android学习笔记(八二):了解Package(1):包和进程

    文章转载只能用于非商业性质,且不能带有虚拟货币.积分.注册等附加条件.转载须注明出处:http://blog.csdn.net/flowingflying/ 在之前,我们已经学习了如何签发apk,见P ...

  4. git学习4 常用命令

    1:更新: 更新后,更新只在Workspace中,没有到暂存区.git status可以查看当前状态. git add <file>  可以放到待提交区.      git  checko ...

  5. [转]eclipse 设置默认编码为Utf-8

    参考:http://www.cnblogs.com/yimu/archive/2011/06/30/SXLYLOVE.html 需要设置的几处地方为: Window->Preferences-& ...

  6. Python打造一个目录扫描工具

    目标:用Python3写一款小型的web目录扫描工具 功能:1.扫描指定站点 2.指定网站脚本类型来扫描 3.可控线程 4.可保存扫描结果 首先定义一个命令参数的函数 def parse_option ...

  7. UML在需求分析阶段的应用

    转自:https://www.cnblogs.com/fuhaots2009/p/3430666.html 上一篇博客写了uml在软件开发过程中的应用,这以篇要详细介绍一下UML在需求分析过程中的应用 ...

  8. UAC(User Agent Client) 和 UAS(User Agent Server)

    SIP协议采用Client/Server模型.每一个请求(Request)触发服务器的一个操作:每个操作被称为方法(Method):每个请求除了指明具体方法外,还携带了一系列的头域(Header fi ...

  9. <c:set var="ctx" value="${pageContext.request.contextPath}" />的学习

    ${pageContext.request.contextPath},是获取当前根目录 set var="ctx",是给这个路径定义了一个变量,用的时候可以通过EL表达式获取:${ ...

  10. startactivityforresult使用

    与startactivity基本相同,不过需要传入(intent,int)第二个int为请求ID,用来识别 在该activity中还应该重写nActivityResult(int requestCod ...