字符串最小周期
来源:互联网 发布:cs1.5百度软件 编辑:程序博客网 时间:2024/05/17 04:37
1. 问题描述
给定一个长度为n的字符串S,如果存在一个字符串T,重复若干次T能够得到S,那么,S叫做周期串,T叫做S
的一个周期。如:字符串abababab是周期串,abab、ab都是它的周期,其中,ab是它的最小周期。设计一个算法,
计算S的最小周期。
2. 问题分析
首先我们会很容易想到最简单的暴力求解:假设字符串S的长度为 len,S的最小周期的长度为 i ,字符串S表示
为S[0…len-1]。
① 我们遍历 i 从1到 len/2 (只需要遍历到 len/2 即可,因为最小周期的长度不可能超过字符串S长度的一半),若len %
i == 0,i 便有可能是最小周期的长度,具体是不是还需要进行判断。
② 判断 i 是不是最小周期的长度,要将字符串S后面的 len – i 个字符中每 i 个字符分成的各组字符分别与前 i 个字符
进行比较,即将 S[i…2i-1],S[2i…3i-1],……,S[len-i…len-1] 各个分组的每个字符分别与 S[0…i-1] 中的每个字符进
行比较。更简洁一点,省去分组的麻烦,直接将 S[i…len-1] 的每个字符 S[j] (j = i,…,len-1) 分别与 S[j % i] 进行比较即
可。
③ 若比较的过程中出现字符不相等,则表示 i 并不是字符串S的周期长度,做 i++后判断转步骤①判断是否是可能的周期,若是则转入步骤②继续判断,否则继续迭代。若比较过程中每个字符判断均相等,则 i 就是字符串S的最小周期长度,停止进行判断。
④ 若 i 遍历到 len/2 在步骤③的比较过程中仍出现字符不相等,则说明字符串S不是周期串,此时返回最小周期长度
为0来表示S不是周期串。
暴力求解具体的C++代码如下:#include <iostream>#include <cstring>using namespace std;int MinCycle(char *s){int i, j;int flag = 0;int slen = strlen(s);for (i = 1; i <= slen/2; i++){if (slen % i == 0){flag = 1;for (j = i; j < slen; j++){if (s[j] != s[j % i]){flag = 0;break;}}}if (flag == 1){return i;}}return 0;}我们可以进行测试,输入s[] = "abccabccabcc",加上main函数如下:
int main(){char s[] = "abccabccabcc";int mclen = MinCycle(s);if (mclen == 0){cout << "字符串不是周期串";}else{cout << "字符串的最小周期是: "; for (int i = 0; i < mclen; i++){cout << s[i];}}cout << endl;return 0;}运行输出结果为:
若把s改成s[] = "abcccabccabcc",运行输出结果为:
我们看到暴力求解用到两个 for 循环,时间复杂度可以认为是O(n^2),相对较高。
3. 优化解法
事实上有一个优化的解法,可将时间复杂度降为O(n)。我们先直接给出求解方法,再进行证明。
利用KMP算法中的next数组进行求解:
① 计算字符串S的 next 数组。此处计算 next 数组使用的是非优化的计算 next 的算法,并且要计算到 len,虽然并
不存在第 len号元素。
② 记k = next[len],mclen = len-k;若 len % mclen == 0 并且 mclen ≠ len,则 mclen 即为S的最小周期长度,前
mclen个字符就是最小周期。
证明:
考察字符串S的k前缀S-front和k后缀S-back,S-front和S-back对应的元素是相同的,假设len % mclen == 0,记m =
len/mclen,看下面的示意图,图中字符串表示框内标注的Sj (j = 1,2,3,…,m-2,m-1,m)表示整个字符串S的第j个分组,
当然所有分组的长度都为mclen。
对于S-front 的前 mclen 个字符S1和S-back 的前 mclen 个字符S2,可得出S1和S2的元素是相同的,我们用 S2 == S1表示;
对于S-front 的前 2*mclen 个字符S1+S2和S-back 的前 2*mclen 个字符S2+S3,由S2 == S1可得出S3 == S2;
对于S-front 的前 3*mclen 个字符S1+S2+S3和S-back 的前 3*mclen 个字符S2+S3+S4,由S2 == S1、S3 == S2可得出S4 == S3;
……
对于S-front 的前 (m-2)*mclen 个字符S1+S2+S3+…+Sm-2和S-back 的前 (m-2)*mclen 个字符S2+S3+S4+…+Sm-1,由S2 == S1、S3 == S2、S4 == S3、… 可得出Sm-1 == Sm-2;
对于S-front的前 (m-1)*mclen 个字符S1+S2+S3+…+Sm-1和S-back的前(m-1)*mclen个字符S2+S3+S4+…+Sm,由S2 == S1、S3 == S2、S4 == S3、…、Sm-1 == Sm-2可得出Sm == Sm-1。
根据以上分析,得出 Sm == Sm-1 == Sm-2 == … ==S3 == S2 == S1,即表示字符串S是由m个相同的子字符串组成的且长度都为 mclen。因此可以证明出 mclen 即为S的最小周期长度。
利用next求解字符串最小周期的C++代码如下:
#include <iostream>#include <cstring>using namespace std;void GetNext(char *s, int next[]){next[0] = -1;int k = -1;int j = 0;int slen = strlen(s);while (j < slen){if (k == -1 || s[j] == s[k]){++k;++j;next[j] = k;}else{k = next[k];}}}int MinCycle(char *s){const int num = 255;int next[num];GetNext(s, next);int len = strlen(s);int mclen = len - next[len];if (len % mclen == 0 && mclen != len){return mclen;}return 0;}进行测试,加上main函数,输入s[] = "abccabccabcc"时,运行结果为:
将s改成s[] = "abcccabccabcc",运行输出结果为:
可以看出和暴力求解得到的结果是一样的。利用 next 数组来求解只有在求解 next 数组用到一层循环,时间复杂度为
O(n),但是由于需要存储 next 数组,因而其空间复杂度上升了,这也就是典型的用空间来换时间。
- 周期字符串的最小周期
- 周期字符串的最小周期
- 字符串最小周期问题
- 字符串最小周期
- 字符串最小周期
- 周期串 字符串的最小正周期
- 字符串最小周期串问题
- kmp求字符串最小周期
- 求字符串的最小周期
- 求字符串的最小周期
- 字符串最小周期串问题
- 输入字符串,求期最小周期
- 求一个字符串的最小正周期
- 求字符串的最小正周期
- 求字符串的重复子字符串的最小周期
- POJ--2406Power Strings+KMP求字符串最小周期
- poj 2406 KMP算法求字符串的最小周期
- 【华为练习题 】 字符串的最小周期(中级)
- oracle表分区详解
- 分分钟学会使用 Git 备份 Linux 配置文件
- 用Tomcat和Eclipse开发Servlet程序
- Java 7之多线程线程池 - 线程池原理(2)
- Android统计图表MPAndroidChart
- 字符串最小周期
- php与c++一些共同性!
- 面向对象三大特性,五大原则
- Java 7之多线程线程池 - Callable和Future
- trigger 学习
- Android安装APK时出现Local path doesn't exist.错误
- 梦一样——十一月英语总结
- perl: warning: Setting locale failed.引发的问题
- 排序算法之梳排序