解题报告 (六) KMP

一、KMP 算法讲解

夜深人静写算法(KMP)

二、KMP 解题报告

HDU 1711 Number Sequence

  • 链接:HDU 1711 Number Sequence
  • 题意:求一个整数串在另一个整数串中第一次出现的位置,找不到输出 -1;
  • 难度:★☆☆☆☆
  • 题解:KMP 模板题,核心逻辑是 KMP 匹配到匹配串结尾时通过匹配串长度算出目标串起始位置;
	if (MPos == MLen - 1) {
    
    
		return TPos - (MLen - 1) + 1; // 下标从1开始,所以+1
	}

HDU 2087 剪花布条

  • 链接:HDU 2087 剪花布条
  • 题意:求一个 目标串 里面有多少个不重叠的 匹配串;
  • 难度:★☆☆☆☆
  • 题解:KMP 模板题,核心逻辑是 KMP 匹配到匹配串结尾时从 NULL_MATCH 开始重新匹配;
	if (MPos == MLen - 1) {
    
    
		MPos = NULL_MATCH;
		++ans;
	}

PKU 3461 Oulipo

  • 链接:PKU 3461 Oulipo
  • 题意:求一个 目标串 里面有多少个可重叠的 匹配串;
  • 难度:★☆☆☆☆
  • 题解:KMP 模板题,核心逻辑是 KMP 匹配到匹配串结尾时从 next[MPos] 开始重新匹配;
	if (MPos == MLen - 1) {
    
    
		MPos = next[MPos];
		++ans;
	}

HDU 2203 亲和串

  • 链接:HDU 2203 亲和串
  • 题意:问 匹配串 能否在 目标串 进行循环移位后找到;
  • 难度:★☆☆☆☆
  • 题解:将目标串拷贝一份套用KMP即可,需要注意的是,如果 匹配串 长度 大于 目标串 需要预判,这种情况一定是找不到的;

HDU 1238 Substrings

  • 链接:HDU 1238 Substrings
  • 题意:给定一系列字符串,找到一个最长的子串,满足它(或者它的逆序串)是所有字符串的子串;
  • 难度:★☆☆☆☆
  • 题解:暴力枚举长度,从长到短枚举所有的字符串的子串,分别做 KMP 匹配即可,找到一个立即返回;

HDU 2328 Corporate Identity

  • 链接:HDU 2328 Corporate Identity
  • 难度:★☆☆☆☆
  • 题解:类似 HDU 1238 Substrings 暴力找子串,再用 KMP 匹配;

HDU 2594 Simpsons’ Hidden Talents

  • 链接:Simpsons’ Hidden Talents
  • 题意:给定两个字符串 A 和 B,求一个既是A的前缀,又是B的后缀的子串,并且保证这个串最长;
  • 难度:★☆☆☆☆
  • 题解:将两个字符串收尾拼接后,用一个未出现过的字符拼接,求一次 next 数组即可;

PKU 2406 Power Strings

  • 链接:PKU 2406 Power Strings
  • 题意:如果一个串 A 可以表示成 XK (X为多个字符的集合,K > 1)的形式,则称 A 为 叠串;给定一个字符串,输出最大的 K;
  • 难度:★★☆☆☆
  • 题解:参见 夜深人静写算法(KMP) 对于 叠串 的讲解;

HDU 1358 Period

  • 链接:HDU 1358 Period
  • 题意:给定一个字符串,要求出字符串的所有前缀中能够表示成 AK 的形式的前缀,并且保证 K > 1 ,而且 K 尽量大,输出所有的 前缀长度 和 对应的 K;
  • 难度:★★☆☆☆
  • 题解:
i 0 1 2 3 4 5 6 7
M[i] a a s a a a s a
next[i] -1 0 -1 0 1 1 2 3
i - next[i] 1 1 3 3 3 4 4 4
  • 首先来看 K = 2 的情况,那么一定满足:
	i - next[i] == next[i] - (-1);
  • 再来看 K = 3 的情况,同理可得:
	i - next[i] == next[i] - next[ next[i] ] == next[ next[i] ] - (-1);
  • 继续归纳 K = 4,5,6… 的情况,可以得出,只要 i - next[i] 能够被 next[i] - (-1) 整除,那么这个除出来的商就是再加上 1 就是所求的 K 了;
  • 所以第 i 个位置的 Ki 的值 = (next[i] - (-1)) / (i - next[i]) + 1;

HDU 3336 Count the string

  • 链接:HDU 3336 Count the string
  • 题意:给定一个字符串,要求求出它所有的前缀在这个字符串出现次数的和,模上 10007;
  • 难度:★★☆☆☆
  • 题解:拿 “ababab” 举例,如下:
i 0 1 2 3 4 5
s a b a b a b
next[i] -1 -1 0 1 2 3
a +1 +1 +1
ab +1 +1 +1
aba +1 +1
abab +1 +1
ababa +1
abababa +1
  • 图中的 +1 代表了 以当前位置结尾的后缀为对应的前缀贡献了几次匹配;
  • 以第 i 个字符结尾的字符串中必定有一个 next[i] 的前缀,即 第 next[i] 的位置为 i 位置贡献 1,画出 next 图就是:
+1
+1
+1
+1
+1
+1
0
1
2
3
4
5
-1
  • 所以可以列出状态转移方程:
    d p [ i ] = { 1 n e x t [ i ] = = − 1 d p [ n e x t [ i ] ] + 1 n e x t [ i ] < > − 1 dp[i] = \begin{cases} 1 &&next[i] == -1\\ dp[ next[i] ] + 1 &&next[i] <> -1 \end{cases} dp[i]={ 1dp[next[i]]+1next[i]==1next[i]<>1

HDU 4300 Clairewd’s message

  • 链接:HDU 4300 Clairewd’s message
  • 题意:给出一个密文转换表,再给出一个串,串包含所有的密文和部分的明文,求最短的可能的包含密文和明文的完整串;
  • 难度:★★★☆☆
  • 题解:对整个串利用密文转换表进行解密操作,然后把 加密串作为 目标串 T,解密串作为 匹配串 M 计算一次 KMP,需要注意的是,给出的串包含全部的密文和部分的明文,所以至少密文的部分占总长度的一半以上;所以匹配的时候要从 (Len+1)/2 的位置开始匹配;当 KMP 匹配到结尾,最后得到的M[ 0 … MPos ] 就是 密文的开头 和 明文的结尾,输出的时候进行一下处理即可;

PKU 2752 Seek the Name, Seek the Fame

  • 链接:PKU 2752 Seek the Name, Seek the Fame
  • 题意:输出所有给定串的 前后缀 的长度;
  • 难度:★★★☆☆
  • 题解:对整个字符串求一遍 next 数组,然后从结尾字符往前一直求 next 并且记录输出即可;
  • 例如,对于字符串 “ababcababababcabab” ,可以画出 next 图如下:
1
3
8
17
-1
  • 所以输出为 2 4 8 18 (输出的是长度,需要 next[i] + 1);

HDU 4763 Theme Section

  • 链接:HDU 4763 Theme Section
  • 题意:给定一个长度为N(1 <= N <= 106)的字符串S,问能否和模式串 EAEBE 进行匹配其中A和B表示任意随机字符,如果能匹配,输出E的最大可能长度,不能匹配输出0。
  • 难度:★★★☆☆
    题解:PKU 2752 的加强版;首先通过 next 数组,求出所有有可能产生 前后缀 的位置,然后在中间剩余的子串中匹配前缀;
  • 假设当前枚举长度为 i,那么在S[i … N-1-i] 中如果能够找到一个长度为 i 的子串满足和 S[0…i-1] 完全匹配,那么 i 就是一个可行解,又因为枚举是从大到小进行的,所以 i 就是E可能的最大长度。
  • 于是问题就转变成了判断 S[i … N - 1 - i] 中是否存在一个和 S[0…i-1] 完全匹配的子串。
  • 如果存在,那么必定存在一个 k ( 2 ∗ i − 1 < = k < = N − 1 − i ) k( 2*i - 1<= k <= N-1-i ) k(2i1<=k<=N1i),使得 S [ k − i + 1... k ] = = S [ 0... i − 1 ] S[k-i+1 ... k] = = S[0 ... i-1] S[ki+1...k]==S[0...i1],所以必定有 N e x t [ N e x t [ . . . [ k ] ] ] = = i − 1 Next[Next[...[k]]] = = i - 1 Next[Next[...[k]]]==i1,所以我们可以预先将S[i … N-1-i]区间内所有的Next值退化后进行Hash,然后在枚举某个长度i的时候去Hash数组中找i是否被标记,如果被标记说明存在某个k满足 S [ k − i + 1... k ] = = S [ 0... i − 1 ] S[k-i+1 ... k] = = S[0 ... i-1] S[ki+1...k]==S[0...i1],i 就是最大可能长度。

PKU 3690 Constellations

  • 链接:PKU 3690 Constellations
  • 题意:给定 N * M (N <= 1000, M <= 1000) 的01矩阵S,再给定T(T <= 100) 个 P * Q (P <= 50, Q <= 50) 的01矩阵,问P*Q的矩阵中有多少个是S的子矩阵。
  • 难度:★★★☆☆
  • 题解:由于P <= 50,所以我们可以把所有 P * Q 的矩阵进行二进制位压缩,将 P * Q 的矩阵的每一列压缩成一个64位整数,这样 P * Q 的矩阵就变成了一个长度为Q的整数序列T,用同样的方式对 N * M 的矩阵进行压缩,总共可以产生 (N-P+1) 个长度为M的整数序列,剩下的就是进行最多 (N-P+1) 次KMP匹配了。

HDU 3746 Cyclic Nacklace

  • 链接:HDU 3746 Cyclic Nacklace
  • 题意:给定一个长度为 N (N <= 105) 的字符串S,求在它的末尾添加几个字符使得他变成一个至少重复两次的连续重复串,要求添加的字符数最少。
  • 难度:★★★☆☆
  • 题解:将字符串当成是 X…XY 的形式来,其中 X 字符串的长度大于Y,Y 字符串的长度可能为 0;然后枚举的是 X 字符串的长度 XLen;
  • 可以得到一些数据:
    X 重 复 的 次 数 K = f l o o r ( M L e n / X L e n ) X重复的次数 K = floor(MLen / XLen) XK=floor(MLen/XLen)
    Y 的 长 度 Y L e n = M L e n − X L e n ∗ K Y的长度YLen = MLen - XLen * K YYLen=MLenXLenK
    然后根据叠串公式判断前缀 S [ 0... X L e n ∗ K − 1 ] S[0 ... XLen * K-1] S[0...XLenK1] 是否是一个 X 的叠串;如果是,判断剩余的 Y 字符串是否为 X 的前缀,这一步可以通过预处理 next 数组来做;最后计算一个最小的 XLen - YLen 就是答案;

猜你喜欢

转载自blog.csdn.net/WhereIsHeroFrom/article/details/108947540
kmp