最小表示法

来源:互联网 发布:阿里巴巴java招聘 编辑:程序博客网 时间:2024/05/16 18:34

最小表示法就是找出字符串S的的循环同构串中字典序最小的一个。

那么什么是循环同构串呢。是

--S=bcad,且S’S的循环同构的串。S’可以是bcad或者cadb,adbc,dbca

即在字符串S中从i>=0开始,从i循环到字符串末尾,再从头循环到i,所形成的字符就是S循环同构串。


因为这样的同构串不止一个,所以我们要找出其中字典序最小的一个(即字符串从小到大排序,其中最小的一个)

为什么要找出其中最小的一个呢?我也不知道诶

最小表示法其实就是找到位置i,从这个位置输出S,使得到的同构串字典序最小。

朴素算法的思想就是

让i=0;j=1(i和j<str.size());然后开始比较,即比较从i发起循环的同构串与j发起循环的同构串那一个比较小

如果

str[i]<str[j];//j++,让j=2发起循环的同构串与i=0发起的同构串比较

如果

//我们要让i发起循环的串最小str[i]>str[j];//说明i发起循环的串大于j发起的令i=j;j=j+1;

如果

str[i] ==str[j];//就引入一个指针k,令k自加比较由i发起循环的串和j发起的串的余下字符的大小//直到str[i+k]!=str[j+k]或者k<str.size()//如果是因为k<str.size();//这个原因退出循环,那么两个同构串相等,比较下一个,即令j++;//如果是因为str[i+k]<str[j+k];//令j++;//如果是因为str[i+k]>str[j+k];//令i=j;j++;

下面是代码:

//寻找字典序循环同构串#include<iostream>#include<string>using namespace std;int find_min_subs(string &str);int main(){string str;while(cin>>str){int index = find_min_subs(str);cout<<index<<endl;cout<<&str[index];string str2(str.begin(),str.begin()+index);cout<<str2<<endl;}}int find_min_subs(string &str)//朴素的寻找法{int i;int j;int k;i = 0;j = 1;k = 1;for(;i<str.size() && j<str.size() ;){if(str[i] > str[j])//大于,说明以j开始的串比较小,我们已该串为标准,再找出比它还小的{i = j;j = j+1;}else if(str[i] < str[j])//说明j指向的串不是最小的,比较下一个{++j;}else//相等,不能判断两个字符串哪个小,比较下一位{k=1;while(k<str.size()){if(str[(i+k)%str.size()] == str[(j+k)%str.size()])k++;else if(str[(i+k)%str.size()] < str[(j+k)%str.size()]){j++;break;}else{i=j;j=j+1;break;}}if(k==str.size())//表明两个同构串相等,取下标比较小的那一个j++;}}return i;//返回从第i个字符开始时str的最小表示}

看了下算法,发现如果输入是aaaaab时,算法效率会退化为O(n^2)

所以改进了下算法

实现方法:

(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]的关系有三种:

  1).s[p1+k]>s[p2+k],p1滑动到p1+k+1处,s[p1--p1+k-1]不会是循环字符串的"最小表示"的前缀。

  2).s[p1+k]<s[p2+k],p2滑动到p2+k+1处。

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

  若滑动后p1==p2,将正在变化的那个指针在+1.直到p1,p2把整个字符串都检验完毕,返回两者中小于len的值。

(3).如果 k==len,则返回min( i , j )

  如果 p1>=len,返回 p2
  如果 p2>=len,返回p1当中最小的
}


int find_min_subs(string &str)//改进的算法{int i = 0;int j = 1;int k = 0;int len = str.size();while(i<len && j<len && k<len ){int t = str[(i+k)%len] - str[(j+k)%len];if(t == 0)k++;else{if(t>0)i+=(k+1);elsej+=(k+1);if(i == j)j++;k=0;}}return i<j?i:j;//返回i,中最小的一个


0 0
原创粉丝点击