链接
字符串的最小表示法
这个算法要做的事请就是把一个字符串首尾相接形成一个环,然后找一个位置切开,使得切开后形成的字符串字典序最小
基于的原理很简单,当我比较两个开头 i , j i,j i,j所形成的字符串时,如果在第 k k k个位置开始不相同,那么字典序较大的那个串所对应的指针就可以直接后移 k k k,均摊时间复杂度 O ( n ) O(n) O(n)
特殊情况:如果比较的过程中两个字符串相同了,那么这个字符串一定就是字典序最小的。因为出现这种情况时,这个串一定是由一种更短的小串 r e p e a t repeat repeat若干次得到的,根据算法的过程就可以推出这个时候 i i i和 j j j都指向字典序最小的开头。
具体请看代码:
struct minimal_cyclic_expression
{
int w[maxn<<1];
int run(char* s, int n)
{
int i=1, j=2, k;
rep(k,n)w[k]=w[n+k]=s[k];
while(i<=n and j<=n)
{
for(k=0;k<n and w[i+k]==w[j+k];k++);
if(k==n)return i;
if(w[i+k]<w[j+k])j = j+k+1==i ? i+1 : j+k+1;
else i = i+k+1==j ? j+1 : i+k+1;
}
return min(i,j);
}
}mce;
题解
求出两个串的最小循环表示然后比较一下就结束了
代码
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#define iinf 0x3f3f3f3f
#define linf (1ll<<60)
#define eps 1e-8
#define maxn 1000010
#define maxe 1000010
#define cl(x) memset(x,0,sizeof(x))
#define rep(_,__) for(_=1;_<=(__);_++)
#define em(x) emplace(x)
#define emb(x) emplace_back(x)
#define emf(x) emplace_front(x)
#define fi first
#define se second
#define de(x) cerr<<#x<<" = "<<x<<endl
using namespace std;
using namespace __gnu_pbds;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
ll read(ll x=0)
{
ll c, f(1);
for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-f;
for(;isdigit(c);c=getchar())x=x*10+c-0x30;
return f*x;
}
struct minimal_cyclic_expression
{
int w[maxn<<1];
int run(char* s, int n)
{
int i=1, j=2, k;
rep(k,n)w[k]=w[n+k]=s[k];
while(i<=n and j<=n)
{
for(k=0;k<n and w[i+k]==w[j+k];k++);
if(k==n)return i;
if(w[i+k]<w[j+k])j = j+k+1==i ? i+1 : j+k+1;
else i = i+k+1==j ? j+1 : i+k+1;
}
return min(i,j);
}
}mce;
int n, m;
char s[maxn], t[maxn];
int main()
{
scanf("%s%s",s+1,t+1);
n=strlen(s+1), m=strlen(t+1);
if(n!=m){printf("No");return 0;}
int a=mce.run(s,n), b=mce.run(t,n);
for(int i=0;i<n;i++)
{
char c1=s[a+i>n?a+i-n:a+i], c2=t[b+i>n?b+i-n:b+i];
if(c1!=c2){printf("No");return 0;}
}
printf("Yes\n");
for(int i=0;i<n;i++)putchar(s[a+i>n?a+i-n:a+i]);
return 0;
}