算法基础之字符串2

来源:互联网 发布:手机迅雷提示无网络 编辑:程序博客网 时间:2024/06/03 21:33

最长回文子串

 用一种不考虑奇数还是偶数的算法进行转换,这里用Manacher算法求最长回文子串,时间复杂度是线性O(N)

         首先通过在每个字符两边都插入一个特殊的符号,使得字符串都转换成奇数字符串。例如:abcde,添加后#a#b#c#d#e#  ;  abcd,添加后#a#b#c#d#

         此外,为了进一步减少编码的复杂度,可以在字符串的开始加入另一个特殊字符,这样就不用特殊处理越界问题,比如$#a#b#a#。

以字符串 12212321 为例,插入#和$这两个特殊符号,变成了 S[] = "$#1#2#2#1#2#3#2#1#",

然后用一个数组 P[i] 来记录以字符 S[i]为中心的最长回文子串向左或向右扩张的长度(包括S[i])。

比如 S 和 P 的对应关系:

S # 1 # 2 # 2 # 1 # 2 # 3 # 2 # 1 #

P 1 2 1 2 5 2 1 4 1 2 1 6 1 2 1 2 1

可以看出, P[i]-1 正好是原字符串中最长回文串的总长度,为 5

接下来怎么计算 P[i]呢? Manacher 算法增加两个辅助变量 id 和 mx,其中id 表示最大回文子串中心的位置, mx 则为 id+P[id],也就是最大回文子串的边界。得到一个很重要的结论:如果 mx > i,那么 P[i] >= Min(P[2 * id - i], mx - i).

下面,令 j = 2*id - i,也就是说 j 是 i 关于 id 的对称点。

当 mx - i > P[j] 的时候,以 S[j]为中心的回文子串包含在以 S[id]为中心的回文子串中,由于 i 和j 对称,以 S[i]为中心的回文子串必然包含在以 S[id]为中心的回文子串中,所以必有 P[i] = P[j]


当 P[j] >= mx - i 的时候,以 S[j]为中心的回文子串不一定完全包含于以 S[id]为中心的回文子串中,但是基于对称性可知,下图中两个绿框所包围的部分是相同的,也就是说以 S[i]为中心的回文子串,其向右至少会扩张到 mx 的位置,也就是说 P[i] >= mx - i。至于 mx 之后的部分是否对称,再具体匹配。


此外,对于 mx <= i 的情况,因为无法对 P[i]做更多的假设,只能让 P[i] = 1,然后再去匹配。

也就是说以 S[i]为中心的回文子串,其向右至少会扩张到 mx 的位置,也就是说 P[i] >= mx - i。至于 mx 之后的部分是否对称,再具体匹配。

核心代码:

int p[100],mx,i,id;memset(p,0,sizeof(p));for(i=1;s[i]!='\0';i++){    p[i]=mx>i?Min(mx-i,P[2*id-1]):0;    while(s[i+p[i]]==s[i-p[i]])    p[i]++;    if(i+p[i]>mx)    {    mx=i+p[i];    id=i;    }}

字符串的全排列

next_permutation算法流程(二找,一换,一翻转)

1.      二找:从右往左找出字符串中有升序的字符x=p[i];找出p[i]右边字符中最后一个比p[i]大的字符y=p[j]

2.      一换:p[i]和p[j]进行互换

3.      一翻转:p[i]位置后的字符串进行翻转

 

核心代码:

bool CalcAllPermutation(char *perm,int num){   int i,k;   //1.1从右往左找出第一个升序的字符   for(i=num-2;i>=0 && perm[i]>perm[i+1];i++);   if(i<0)     return false;   //1.2找出该位置后最后一个比perm[i]大的字符   for(k=num-1;k>=0 && perm[k]<=perm[i];k++);   //2.交换   swap(perm[k],perm[i]);   //3.翻转   reverse(perm+i+1,perm+num);   return true;}

由于全排列总共有 n!种排列情况,花的时间复杂度O(n!)



0 0
原创粉丝点击