最小表示法就是找出字符串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);
else
j+=(k+1);
if(i == j)
j++;
k=0;
}
}
return i<j?i:j;//返回i,中最小的一个