字符串最小表示法

给定一个环形字符串, 显然可以有 n 种表示方法, 字典序最小的就称为这个环的最小表示法。

设 B[i] = S[i~n] + S[1~i-1], 把 S 复制一份接到它的尾部, 得到 SS, 显然 B[i] = SS[i~i+n-1]。

假设 B[i] 与 B[j] 比较, 第一次发现不同的地方是 SS[i+k] > SS[j+k], 那么除了 B[i] 不可能是最小表示法以外, B[i+1] ~ B[i+k] 都不可能是最小表示法。

int n = strlen(s + 1);
for(int i=1;i<=n;++i) s[n+i]=s[i];
int i=1, j=2, k;
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]) {
        i = i+k+1;
        if(i==j) ++i;
	} else {
        j = j+k+1;
        if(i==j) ++j;
	}
}
ans = min(i,j); // B[ans] 是最小表示

这个其实复杂度是 O(n) 的。

例题:项链Necklace

显然字符串的最小表示是唯一的, 这样, 就可以给字符串做一个唯一表示。

#include<bits/stdc++.h>
using namespace std;
const int L = 1000003;

int n;
char a[L*2+1], b[L*2+1];

int Min(char *s) {
  for(int i=1;i<=n;++i) s[n+i]=s[i];
  int i=1, j=2, k;
  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]) {
      i = i+k+1;
      if(i==j) ++i;
    } else {
      j = j+k+1;
      if(i==j) ++j;
    }
  }
  return min(i,j);
}

int main() {
  scanf("%s",a+1); scanf("%s",b+1); n=strlen(a+1);
  int x=Min(a);
  int y=Min(b);
  for(int i=0;i<n;++i) if(a[x+i]!=b[i+y]) {
    puts("No");
    return 0;
  }
  puts("Yes");
  for(int i=0;i<n;++i) putchar(a[x+i]);
  return 0;
}
posted @ 2020-09-01 18:16  xwmwr  阅读(182)  评论(2编辑  收藏  举报