循环字符串的最小表示(自学)

循环字符串的最小表示

drizzlecrj整理

循环同构问题:给出两个串:s1 = “babba”和s2 = “bbaba”,其中两者均看成环状的,即首尾是相接的,问:从s1的哪里断开可以得到和s2一样的串或者两者用不会相同?本题就是从s1的第2个字符’a’后面断开,可以得到与s2一样的串。这个问题就是同构问题。下面一步一步分析:

1.朴素算法(O(nm)):即尝试s1的n个断开点,与s2进行比较,如果相同则找到同构位置,否则找不到。该算法仅适用于n, m规模较小情况,对于n, m 都在10000规模的长度,明显速度太慢。

2.转换为模式匹配:对于此类问题,已经有一个很好的转换思路了,即:首先构造新的模型:S=s1+s1为主串,s2为模式串。如果s1和s2是循环同构的,那么s2就一定可以在S中找到匹配。否则找不到匹配则两则不能同构。而在S中寻找s2的匹配是有很多O(N+M)级的算法了,KMP算法就是这样一个优秀的算法,所以本问题转换为模式匹配后应用KMP算法,可以在O(n+m)的时间内获得问题的解。

3.下面再来看看“最小表示法”在此类问题中的应用(算法思路来源于国家队的一位队员--周源),它也可以在O(n+n)时间内求解,更大的优势还有无需KMP算法的Next数组,仅需要两个指针即可。

一.从小问题谈起:

问题:有两列数a1,a2…an和b1,b2…bn ,不记顺序,判断它们是否相同。eg:{an} = {2, 3, 5, 7};{bn} = {2, 7, 5 3}。一眼可见两者是相同的,但是对于计算机来说,如果采用枚举算法,那么比较次数将是:n*(n-1)*(n-2)….*2*1 = n! 量级。阶乘增长之快,时间上是无法忍受的。抓住问题的本质:如果两列数相同,那么它们排序之后从头到尾肯定一样!则问题在O(nlogn)时间解决。可见这个问题的本质就在于排序(非降序)之后的序列是原序列的“最小表示”,如果两个序列的“最小表示”相同则两者就相同,否则就不相同。

启示:当两个对象有多种形式且需判断它们在某种变换规则下是否相同时,可转换为比较可以通过变换规则得到的所有表示的“最小表示”是否相同。例如本问题是不计顺序的规则,最小表示就是非降序排列后的序列。当然对于其它问题就不能简单的排序了,需要满足“在变换规则下可以达到的表示形式”。

二.最小表示法

1.首先定义一个变换规则f,判断两个事物s和t是否互为f本质相同,方法就是:将s,t转换为规则f下的最小表示形式,再比较是否相同。

2.返回到本节一开始提到的字符串循环同构问题,规则f就是“循环移动”,最直接最简单

的方法就是分别求出s1, s2的最小表示,再比较两者是否相同,但是可以发现这明显也是一个O(n^2)思路。

3.换一种思路,令Min(s)返回值为:从s的第Min(s)个字符引起的s的一个循环表示的最小表示,若有多个值则返回下标最小的一个。例如s = {bbbaab},那么Min(s) = 3 --下标从0算起。Min(s)的求法在后面部分具体阐述,这个问题仅仅用到这个概念,不必直接求,因为当找到循环同构时实际就是Min(s)。

4.先类似于模式匹配方法将两者加倍:u = s1 + s1,w = s2 + s2,指针i,j分别指向u, w的第一个位置0,i,j指针滑动可以得到两种情况:

(1) 若i,j分别指向Min(s1)和Min(s2)时,一定有u[i…i+|s1|-1] = w[j…j+|s2|+1]也就是立马得到解。

(2) 否则i

因此问题转换为:两个指针分别向后滑动比较,如果比较失败,如何正确的滑动指针使得新指针i, j仍然满足:i=0),即有u[i+k]≠w[j+k]。仍然分两种情况:

(1) u[i+k] > w[j+k]:令iw[j+k],因此整个s[x-1]都不是s1的最小表示的前缀,则Min(s1)>i+k,所以i可以滑动到i+k+1仍可保证

(2) 同样的分析,如果u[i+k]

综上,也就是说,两指针向后滑动比较失败以后,指向较大字符的指针向后滑动k+1个位置,直到出现如下情况:

(1) 最后两指针指向的位置,向后面(包括自身起始位置)比较了len个字符都匹配成功时,那两个位置i, j就分别是s1和s2的“最小表示”的位置;

(2) 或者i, j两个指针其中一个移动到>= len的位置,那么表示s1和s2无法循环同构。以实例分析,假设s1=‘babba’,s2=‘bbaba’。那么有

u = ‘b a b b a b a b b a’

w = ‘b b a b a b b a b a’

按照上面分析的方法,i,j指针都指向0,然后滑动,那么在i = 4, j = 2时会出现连续5

个字符相等:u[4→8] = w[2→6],所以s1和s2是循环同构的,同构位置在s1的i = 4,s2的j = 2处。实际也都是它们最小表示的位置。

至此,循环同构的这个O(n)级的算法介绍完毕,不得不说提出这个算法思想的人属于天才级别的,很类似于KMP的思路,但是却仅适用两个指针实现了O(n)的算法。下面将分别就这个算法,展开两个问题的具体求解。

1.求字符串的循环最小表示:

上面说的两个字符串同构的,并没有直接先求出Min(s),而是通过指针移动,当某次匹配串长时,那个位置就是Min(s)。而这里的问题就是:不是给定两个串,而是给出一个串,求它的Min(s),eg:Min(“babba”) = 4。那么由于这里并非要求两个串的同构,而是直接求它的最小表示,由于源串和目标串相同,所以处理起来既容易又需要有一些变化:我们仍然设置两个指针,p1, p2,其中p1指向s[0],p2指向s[1],仍然采用上面的滑动方式:

(1) 利用两个指针p1, p2。初始化时p1指向s[0], p2指向s[1]。

(2) k = 0开始,检验s[p1+k] 与 s[p2+k] 对应的字符是否相等,如果相等则k++,一直下去,直到找到第一个不同,(若k试了一个字符串的长度也没找到不同,则那个位置就是最小表示位置,算法终止并返回)。则该过程中,s[p1+k] 与 s[p2+k]的大小关系,有三种情况:

(A). s[p1+k] > s[p2+k],则p1滑动到p1+k+1处 --- 即s1[p1->p1+k]不会

是该循环字符串的“最小表示”的前缀。

(B). s[p1+k]

(C). s[p1+k] = s[p2+k],则 k++; if (k == len) 返回结果。

注:这里滑动方式有个小细节,若滑动后p1 == p2,将正在变化的那个指针再+1。直到p1、p2把整个字符串都检验完毕,返回两者中小于 len 的值。

(3) 如果 k == len, 则返回p1与p2中的最小值

如果 p1 >= len 则返回p2

如果 p2 >= len 则返回p1

(4) 进一步的优化,例如:p1要移到p1+k+1时,如果p1+k+1

至此,求一个字符串的循环最小表示在O(n)时间实现,感谢大牛的论文。其中实现时的小细节“如果滑动后p1 == p2,将正在变化的那个指针再+1”,开始没有考虑,害得我想了几个小时都觉得无法进行正确的移动。具体例题有两个:http://acm.zju.edu.cn 的2006和1729题。一个是10000规模一个是100000规模。运行时间前者是0S,后者是0.05S。

循环字符串的最小表示

drizzlecrj整理

循环同构问题:给出两个串:s1 = “babba”和s2 = “bbaba”,其中两者均看成环状的,即首尾是相接的,问:从s1的哪里断开可以得到和s2一样的串或者两者用不会相同?本题就是从s1的第2个字符’a’后面断开,可以得到与s2一样的串。这个问题就是同构问题。下面一步一步分析:

1.朴素算法(O(nm)):即尝试s1的n个断开点,与s2进行比较,如果相同则找到同构位置,否则找不到。该算法仅适用于n, m规模较小情况,对于n, m 都在10000规模的长度,明显速度太慢。

2.转换为模式匹配:对于此类问题,已经有一个很好的转换思路了,即:首先构造新的模型:S=s1+s1为主串,s2为模式串。如果s1和s2是循环同构的,那么s2就一定可以在S中找到匹配。否则找不到匹配则两则不能同构。而在S中寻找s2的匹配是有很多O(N+M)级的算法了,KMP算法就是这样一个优秀的算法,所以本问题转换为模式匹配后应用KMP算法,可以在O(n+m)的时间内获得问题的解。

3.下面再来看看“最小表示法”在此类问题中的应用(算法思路来源于国家队的一位队员--周源),它也可以在O(n+n)时间内求解,更大的优势还有无需KMP算法的Next数组,仅需要两个指针即可。

一.从小问题谈起:

问题:有两列数a1,a2…an和b1,b2…bn ,不记顺序,判断它们是否相同。eg:{an} = {2, 3, 5, 7};{bn} = {2, 7, 5 3}。一眼可见两者是相同的,但是对于计算机来说,如果采用枚举算法,那么比较次数将是:n*(n-1)*(n-2)….*2*1 = n! 量级。阶乘增长之快,时间上是无法忍受的。抓住问题的本质:如果两列数相同,那么它们排序之后从头到尾肯定一样!则问题在O(nlogn)时间解决。可见这个问题的本质就在于排序(非降序)之后的序列是原序列的“最小表示”,如果两个序列的“最小表示”相同则两者就相同,否则就不相同。

启示:当两个对象有多种形式且需判断它们在某种变换规则下是否相同时,可转换为比较可以通过变换规则得到的所有表示的“最小表示”是否相同。例如本问题是不计顺序的规则,最小表示就是非降序排列后的序列。当然对于其它问题就不能简单的排序了,需要满足“在变换规则下可以达到的表示形式”。

二.最小表示法

1.首先定义一个变换规则f,判断两个事物s和t是否互为f本质相同,方法就是:将s,t转换为规则f下的最小表示形式,再比较是否相同。

2.返回到本节一开始提到的字符串循环同构问题,规则f就是“循环移动”,最直接最简单

的方法就是分别求出s1, s2的最小表示,再比较两者是否相同,但是可以发现这明显也是一个O(n^2)思路。

3.换一种思路,令Min(s)返回值为:从s的第Min(s)个字符引起的s的一个循环表示的最小表示,若有多个值则返回下标最小的一个。例如s = {bbbaab},那么Min(s) = 3 --下标从0算起。Min(s)的求法在后面部分具体阐述,这个问题仅仅用到这个概念,不必直接求,因为当找到循环同构时实际就是Min(s)。

4.先类似于模式匹配方法将两者加倍:u = s1 + s1,w = s2 + s2,指针i,j分别指向u, w的第一个位置0,i,j指针滑动可以得到两种情况:

(1) 若i,j分别指向Min(s1)和Min(s2)时,一定有u[i…i+|s1|-1] = w[j…j+|s2|+1]也就是立马得到解。

(2) 否则i

因此问题转换为:两个指针分别向后滑动比较,如果比较失败,如何正确的滑动指针使得新指针i, j仍然满足:i=0),即有u[i+k]≠w[j+k]。仍然分两种情况:

(1) u[i+k] > w[j+k]:令iw[j+k],因此整个s[x-1]都不是s1的最小表示的前缀,则Min(s1)>i+k,所以i可以滑动到i+k+1仍可保证

(2) 同样的分析,如果u[i+k]

综上,也就是说,两指针向后滑动比较失败以后,指向较大字符的指针向后滑动k+1个位置,直到出现如下情况:

(1) 最后两指针指向的位置,向后面(包括自身起始位置)比较了len个字符都匹配成功时,那两个位置i, j就分别是s1和s2的“最小表示”的位置;

(2) 或者i, j两个指针其中一个移动到>= len的位置,那么表示s1和s2无法循环同构。以实例分析,假设s1=‘babba’,s2=‘bbaba’。那么有

u = ‘b a b b a b a b b a’

w = ‘b b a b a b b a b a’

按照上面分析的方法,i,j指针都指向0,然后滑动,那么在i = 4, j = 2时会出现连续5

个字符相等:u[4→8] = w[2→6],所以s1和s2是循环同构的,同构位置在s1的i = 4,s2的j = 2处。实际也都是它们最小表示的位置。

至此,循环同构的这个O(n)级的算法介绍完毕,不得不说提出这个算法思想的人属于天才级别的,很类似于KMP的思路,但是却仅适用两个指针实现了O(n)的算法。下面将分别就这个算法,展开两个问题的具体求解。

1.求字符串的循环最小表示:

上面说的两个字符串同构的,并没有直接先求出Min(s),而是通过指针移动,当某次匹配串长时,那个位置就是Min(s)。而这里的问题就是:不是给定两个串,而是给出一个串,求它的Min(s),eg:Min(“babba”) = 4。那么由于这里并非要求两个串的同构,而是直接求它的最小表示,由于源串和目标串相同,所以处理起来既容易又需要有一些变化:我们仍然设置两个指针,p1, p2,其中p1指向s[0],p2指向s[1],仍然采用上面的滑动方式:

(1) 利用两个指针p1, p2。初始化时p1指向s[0], p2指向s[1]。

(2) k = 0开始,检验s[p1+k] 与 s[p2+k] 对应的字符是否相等,如果相等则k++,一直下去,直到找到第一个不同,(若k试了一个字符串的长度也没找到不同,则那个位置就是最小表示位置,算法终止并返回)。则该过程中,s[p1+k] 与 s[p2+k]的大小关系,有三种情况:

(A). s[p1+k] > s[p2+k],则p1滑动到p1+k+1处 --- 即s1[p1->p1+k]不会

是该循环字符串的“最小表示”的前缀。

(B). s[p1+k]

(C). s[p1+k] = s[p2+k],则 k++; if (k == len) 返回结果。

注:这里滑动方式有个小细节,若滑动后p1 == p2,将正在变化的那个指针再+1。直到p1、p2把整个字符串都检验完毕,返回两者中小于 len 的值。

(3) 如果 k == len, 则返回p1与p2中的最小值

如果 p1 >= len 则返回p2

如果 p2 >= len 则返回p1

(4) 进一步的优化,例如:p1要移到p1+k+1时,如果p1+k+1

至此,求一个字符串的循环最小表示在O(n)时间实现,感谢大牛的论文。其中实现时的小细节“如果滑动后p1 == p2,将正在变化的那个指针再+1”,开始没有考虑,害得我想了几个小时都觉得无法进行正确的移动。具体例题有两个:http://acm.zju.edu.cn 的2006和1729题。一个是10000规模一个是100000规模。运行时间前者是0S,后者是0.05S。


相关内容

  • 全国计算机等级二级VB考试题型与解题技巧
  • 一.上机考点 由于上机考试的方式和主要考点没有很大变化,因此可以通过分析历届上机考题来归纳总结上机考试考核的重点,我们下面来介绍近几年二级Visual Basic上机考试所考知识点的分布情况. (1)对象及其操作:控件的画法.基本操作及控件值. (2)数据类型及其运算:涉及到关系运算符.算术运算符. ...

  • 数据结构填空总题目
  • 1. 一个算法应该具有以下几个五个特征:(有穷性)(确定性)(输入)(输出)(可行性) 2. 算法的复杂度有(时间)和(空间)之分 3. 数据结构指的是数据之间的相互关系,,既数据的组织形式,一般包括三个方面的内容(逻辑结构)(存储结构)(数据的运算) 4. (数据元素)是数据的基本单位 5. (算 ...

  • C语言程序设计实验课程简介
  • 四川师范大学计算机科学学院 <C 语言程序设计> 实 验 手 册 2010年2月 年级: 2009级 专业: 计算机科学与技术 班级: 一班 姓名: 谢丹 学号: 2009110156 指导教师: 廖雪花 <C 语言程序设计>实验课程简介 课程名称:C 语言程序设计实验 课程 ...

  • 程序设计基础(知识点)
  • 第三部分 程序设计基础 3.1 程序.程序设计.程序设计语言的定义 ⑴程序:计算机程序,是指为了得到某种结果而可以由计算机等具有信息处理能力的装置执行的代码化指令序列,或者可以被自动转换成代码化指令序列的符号化指令序列或者符号化语句序列. ⑵程序设计:程序设计是给出解决特定问题程序的过程,是软件构造 ...

  • 葵花宝典1.0
  • 葵花宝典 欲练此功,必先自宫 若不自宫,也能成功 前言: 本秘籍只针对现场面试的学员,本教没有搜集到视频面试的资料,所以不做评论,姑且认为与现场面试大同小异. 强烈建议学员尽早提交入学测试(可百度可群里讨教),切不可慢慢悠悠看完视频再做,以免耽误时机,后期面试全被约满.提交测试后应预约较晚的时间面试 ...

  • Win32编程基础知识_天空总是蓝色的
  • Win32编程基础知识 2007年10月03日 星期三 10:56 Win32编程基础知识 正文: 尽管Windows应用程序千变万化,令人眼花缭乱,但,消息机制和窗口过程却始终它们的基础,掌握了这两项技术,也就相当于把握住了问题的关键. 如果你以前是C程序员或是MFC的忠实用户,只要你学习过C语言 ...

  • 程序改错题汇总
  • 程序改错题汇总 1.用" 起泡法" 对连续输入的十个字符排序后按从小到大的次序输出. 2.分别统计字符串中大写字母和小写字母的个数. 3.求1到10的阶乘的和. 4.判断m 是否为素数,若是返回1,否则返回0. 5.用选择法对数组中的n 个元素按从小到大的顺序进行排序. 6.求一 ...

  • 数据结构题库
  • 第一章绪论 一,选择题 1.组成数据的基本单位是(C ) A .数据项B .数据类型C .数据元素D .数据变量 2.数据结构是研究数据的(C )以及它们之间的相互关系. A .理想结构,物理结构B .理想结构,抽象结构 C .物理结构,逻辑结构D .抽象结构,逻辑结构 3.算法分析的两个主要方面是 ...

  • 单片机基础第三版课后答案_李广弟
  • ************************ 第一章: 一.填空题 1.11100EH[**************]0 2.4 3.255-51 4.输入设备 5.84 6.630*8*1024 7.位字节字bitbtypeword 8.[***********]011011 9.11089- ...