zoukankan      html  css  js  c++  java
  • [Aizu] ALDS1_14_B: String Search[续]

    题目

    传送门: ALDS1_14_B: String Search

    描述

    寻找字符串P在一个文本T中出现的位置, 输出所有在T中找到的P的索引, T的索引从0开始.

    输入

    第一行, 给出一个文本T, 在第二行, 给出一个字符串P

    输出

    每行输出一个在T中找到的P的下标, 以升序输出

    限制条件

    • (1 leq length of T leq 1000000)
    • (1 leq length of P leq 10000)
    • 输入由字母字符和数字组成

    样例输入1

    aabaaa
    aa
    

    样例输出1

    0
    3
    4
    

    样例输入2

    xyzz
    yz
    

    样例输出2

    1
    

    样例输入3

    abc
    xyz
    

    样例输出3

     
    

    求解

    分析

    KMP算法讲解参见:KMP算法简明教程
    以下的所有解释都是出自上述文章, 稍有修改, 以自己可以理解的形式重述了一遍.

    KMP算法步骤如下

    1. 初始化i, j均为0
    2. 依次往后比较T[i], P[j], 如果相等, i与j都加一, 否则保持i不变, j=k. 如果j=k之后为-1, 那么i与j都加一
    3. 重复步骤2

    关于k的说明:
    在匹配到T[i] != P[j]时, 假设需要让 j=k, 然后继续匹配, 那么就有T[i]之前的k个字符与P[k]之前的k个字符相同, 记作 T[i - k, i -1] = P[0, k - 1], 由于是在匹配到T[i]与P[j]的时候才不相等, 那么T[i]之前的k个字符肯定与P[j]之前的k个字符相同, 记作 T[i - k, i - 1] = P[j - k, j - 1], 由此, 可以得到新的关系式 P[0, k - 1] = P[j - k, j - 1], 即在P中寻找到最大的k(0 < k < j), 使得前式成立, P中j之前的k个字符与从0开始的k个字符相同.
    递推见设计部分(不是优化版本的, 因为已经很头晕了emmm)

    设计

    记 k = nx[j];
    nx[0] = -1; 配合KMP步骤第2条使用
    nx[1] = 0; 虽然前面有1个匹配到了, 但是如果写成1的话还是比较自己, 所以一般都写0
    设 k = nx[j] 便有 P[0, k - 1] = P[j - k, j - 1]
    如果 P[k] = P[j] 那么就有 P[0, k] = P[j - k, j]
    nx[j + 1] = k + 1;
    如果 P[k] != P[j], 那么令k' = nx[k];
    如果 P[k'] = P[j], 那么 nx[j + 1] = nx[k'] + 1;
    否则重复, 直到 k' = -1 此时 nx[j + 1] = 0;

    我写的如下:

    nx[0] = -1;
    nx[1] = 0;
    for (i = 1; i < P_len; i++) {
    	int k = i;
    	while (nx[k] != -1) {
    		if (P[nx[k]] == P[i]) {
    			break;
    		}
    		k = nx[k];
    	}
    	nx[i + 1] = nx[k] + 1;
    }
    

    编码

    #include <bits/stdc++.h>
    using namespace std;
    #define MAX_P 10005
    #define MAX_T 1000005
    
    char T[MAX_T], P[MAX_P];
    int nx[MAX_P];
    
    int main(void) {
    	ios::sync_with_stdio(false);
    	cin.tie(0);
    	int i, j;
    
    	cin.getline(T, MAX_T);
    	int T_len = cin.gcount() - 1;
    	cin.getline(P, MAX_P);
    	int P_len = cin.gcount() - 1;
    
    	nx[0] = -1;
    	nx[1] = 0;
    	for (i = 1; i < P_len; i++) {
    		int k = i;
    		while (nx[k] != -1) {
    			if (P[nx[k]] == P[i]) {
    				break;
    			}
    			k = nx[k];
    		}
    		nx[i + 1] = nx[k] + 1;
    	}
    	i = 0;
    	j = 0;
    	while (i < T_len) {
    		if (T[i] == P[j]) {
    			i++;
    			j++;
    			if (j == P_len) {
    				cout << i - j << endl;
    				j = nx[j]; // j在++之后不会为0
    			}
    		} else {
    			j = nx[j];
    			if (j == -1) {
    				i++;
    				j++;
    			}
    		}
    	}
    }
    

    结果

    通过了, 不过时间相较别人的比较长

    总结

    之前都不知道这个算法叫什么, 傻傻的看别人的代码, 以为真能悟出来, 最后好久之后才知道了叫做KMP算法, 之后就可以去看思想了, 比只看代码好多了

  • 相关阅读:
    block
    最短路径(图论-北京地铁线路查询)
    Linux与git使用引导(git rm 与rm命令)
    Linux、vim、Makefile-操作系统lab0
    2019-BUAA-OO-第一次作业(表达式函数求导)
    1064
    Navicate 连接mysql问题
    pypi上传问题
    pypi上传命令
    关于 List add方法
  • 原文地址:https://www.cnblogs.com/by-sknight/p/10957338.html
Copyright © 2011-2022 走看看