最小表示法,其实就是给你一个字符串s,让你求从一个点i开始数n个字符(n=|s|),若是最后一个字符,就转到第一个字符数起,让这个数法所得出的字符串s'比其它任意数法的字典序小.
也就像这么个字符串:"SSASBBAS".
那么它的最小表示就是"ASBBASSS",最小表示的第一个字符在第2位(设字符串最前面的字符是第0位).
那么设计一个算法很简单,暴力比较,时间复杂度为O(n^2).
很容易想到破环成链,这样更好处理.
代码如下:
inline int small(string s){ //返回的是第一个字符的位置 int n=s.size(),minn=0; s=s+s; for (int i=1;i+n-1<=s.size();i++){ for (int j=0;j<n;j++) if (s[i+j]>s[minn+j]) break; else if (s[i+j]<s[minn+j]){ minn=i; break; } } return minn; }
那么有没有优化方案呢?
我们考虑,若s[i+j]>s[minn+j],那么s[i+1],s[i+2],...,s[i+j]作为起点的字符串还有没有可能成为最小表示呢?
没有,因为肯定有一个s[minn+1],s[minn+2],...,s[minn+j]更小.
所以算法就好写了.
算法步骤:
1.初始化两个指针i,j,i=0,j=1.
2.比较它们两个串,直到,s[i+p]≠s[j+p].(若跑了p=n=|s|是还相等,说明这是只由一个字符组成的字符串).
3.若s[i+p]>s[j+p],则i=i+p+1,为了保证i≠j,在i=j的时候i++;若s[i+p]<s[j+p],对j进行差不多的操作.
4.若i>=n,返回j;若j>=n,返回i;否则重复2、3两步.
那么代码如下:
inline int small(string s){ //返回的是第一个字符的位置 int i=0,j=1,n=s.size(); s=s+s; while (i<n&&j<n){ for (k=0;k<=n&&s[i+k]==s[j+k];k++); if (k==n) break; if (s[i+k]<s[j+k]){ j=j+k+1; if (j==i) j++; }else if (s[i+k]>s[j+l]){ i=i+k+1; if (i==j) i++; } } return min(i,j); }