本文将用三个算法来解决此题
提示: ( 如果你是顺着INTERMEDIATE往下做的这个题有一个明显的思路)
1. 想办法使用后缀数组 , 因为题目要求的是一种字符串的循环表示形式(AKA 字符串表示法) , 那么可以把字符串写两遍然后跑一跑后缀数组啊
2. 跑出来后SA[1]不一定是答案 , 因为还有可能是后面那个加上字符串的某个后缀 , 所以要找第一个SA小于原来字符串长度的后缀.......
3. 还没完 , 找到后还需要往后一直找 , 找到一个SA最小并且中途的HEIGHT不小于字符串长度的下标....(有点废话是吧)
实现后的代码如下(等会贴出来)
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <string>
#include <vector>
#include <deque>
#include <stack>
#include <algorithm>
using namespace std;
const int maxn = 2e4+1e2;
int s[maxn] , n;
int t1[maxn] , t2[maxn] , c[maxn] , sa[maxn] , height[maxn] , Rank[maxn];
void SA(int m)
{
int *x = t1 , *y = t2;
for(int i=0;i<m;i++) c[i] = 0;
for(int i=0;i<n;i++) c[x[i] = s[i]] ++;
for(int i=1;i<m;i++) c[i] += c[i-1];
for(int i=0;i<n;i++) sa[--c[x[i]]] = i;
for(int k=1;k<=n;k*=2)
{
int p = 0;
for(int i=n-k;i<n;i++) y[p++] = i;
for(int i=0;i<n;i++) if(sa[i]>=k) y[p++] = sa[i]-k;
for(int i=0;i<m;i++) c[i] = 0;
for(int i=0;i<n;i++) c[x[i]] ++;
for(int i=1;i<m;i++) c[i] += c[i-1];
for(int i=n-1;i>=0;i--) sa[--c[x[y[i]]]] = y[i];
swap(x , y);
p=1;
x[sa[0]] = 0;
for(int i=1;i<n;i++)
x[sa[i]] = y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k] ? p-1 : p++;
if(p>=n) break;
m = p;
}
}
void getHeight()
{
for(int i=0;i<n;i++) Rank[sa[i]] = i;
int k = 0 , j;
for(int i=0;i<n-1;i++)
{
if(k) k--;
j = sa[Rank[i]-1];
while(s[i+k] == s[j+k]) k++;
height[Rank[i]] = k;
}
}
int id(char c) { return c-'a'+1; }
char ss[maxn];
int main(int argc, char *argv[]) {
int t;
scanf("%d",&t);
while(t--)
{
scanf("%s",ss);
int len = strlen(ss);
n = 0;
for(int i=0;i<len;i++) s[n++] = id(ss[i]);
for(int i=0;i<len;i++) s[n++] = id(ss[i]);
s[n++] = 0;
SA(28);
getHeight();
for(int i=0;i<n;i++) if(sa[i]<len)
{
int res = sa[i];
while(height[i+1]>=len && i+1<n) { i++; if(sa[i]<len) res = min(res , sa[i]); }
cout<<res+1<<endl;
break;
}
}
return 0;
}
第二个思路 , 后缀自动机...........如果不看不慌不忙的博客我也不知道这个数据结构 , 研究了陈姐姐(CLJ)的论文(自行百度)。 然后顺着自动机找一条最小路径就OK了
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <vector>
#include <deque>
#include <algorithm>
#include <stack>
#include <string>
#include <map>
#include <set>
using namespace std;
const int sigmaSize = 26;
const int maxn = 1e4+1e2;
struct state
{
state* go[sigmaSize] , *pre;
int Max;
state(int _Max = 0): pre(0), Max(_Max) { memset(go , 0 , sizeof(go)); }
};
int sz;
state pool[maxn*3];
inline state* newnode(int _Max = 0) { memset(pool[sz].go , 0 , sizeof(pool[sz].go)); pool[sz].pre = 0; pool[sz].Max = _Max; return &pool[sz++]; }
state *root , *last;
void extend(int w)
{
state* p = last;
state* np = newnode(p->Max+1);
while(p && p->go[w] ==0)
p->go[w] = np , p = p->pre;
if(p)
{
state* q = p->go[w];
if(p->Max+1 == q->Max)
np->pre = q;
else
{
state* nq = newnode(p->Max+1);
memcpy(nq->go , q->go , sizeof q->go );
nq->pre = q->pre;
q->pre = nq;
np->pre = nq;
while(p && p->go[w] == q)
p->go[w] = nq , p = p->pre;
}
}
else np->pre = root;
last = np;
}
int query(int n)
{
state* now = root;
while(n--)
for(int i=0;i<sigmaSize;i++) if(now->go[i]) { now = now->go[i]; break; }
return now->Max;
}
inline int id(char c) { return c-'a'; }
char s[maxn];
int main()
{
freopen("in","r",stdin);
int t , n;
scanf("%d" , &t);
while(t-- && scanf("%s",s))
{
sz = 0;
root = newnode();
last = root;
n = strlen(s);
for(int k=0;k<2;k++) for(int i=0;i<n;i++) extend(id(s[i]));
printf("%d\n",query(n)-n+1);
}
return 0;
}
第三个思路是我测试时间表现最好的算法 , 基于周源的字符串的最小表示法 , 是几乎没有常数的 , 但这个方法对于处理字符串的其他问题是有局限性的
代码如下:(等会贴出)
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <string>
#include <vector>
#include <deque>
#include <stack>
#include <algorithm>
using namespace std;
const int maxn = 1e4+1e2;
char s[maxn];
int main(int argc, char *argv[]) {
int t;
scanf("%d",&t);
while(t--)
{
scanf("%s",s);
int i =0 , j = 0 , n = (int)strlen(s) , k;
while(i<n && j<n)
{
if(i==j) j++;
k = 0;
while(k<=n && s[(i+k)%n]==s[(j+k)%n]) k++;
if(s[(i+k)%n]>s[(j+k)%n]) i+=k+1;
else j+=k+1;
}
printf("%d\n",i+1);
}
return 0;
}
时间从上到下依次变快 , 可以自行比较