洛谷传送门
SPOJ传送门
UVA传送门
题目描述
输入输出格式
####输入格式:
输出格式:
输入输出样例
输入样例#1:
4
helloworld
amandamanda
dontcallmebfu
aaabaaa
输出样例#1:
10
11
6
5
解题分析
求最小表示, 直接将原串两次插入后缀自动机即可。 因为可能开头相同的子串很多, 所以要跳到末尾得到其 l e n len len值后减去字符串长度再加 1 1 1即可。
代码如下:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cctype>
#include <cstdlib>
#define R register
#define IN inline
#define W while
#define MX 40050
char dat[MX];
int cnt, l, cur, last;
int len[MX], par[MX], to[MX][26];
namespace SAM
{
IN void insert(R int id)
{
R int now = last, tar;
cur = ++cnt; len[cur] = len[last] + 1;
for (; (~now) && !to[now][id]; now = par[now]) to[now][id] = cur;
if(now < 0) return par[last = cur] = 0, void(); tar = to[now][id];
if(len[tar] == len[now] + 1) return par[last = cur] = tar, void();
int nw = ++cnt; len[nw] = len[now] + 1;
par[nw] = par[tar], par[tar] = par[last = cur] = nw;
std::memcpy(to[nw], to[tar], sizeof(to[nw]));
for (; (~now) && to[now][id] == tar; now = par[now]) to[now][id] = nw;
}
IN void calc()
{
R int now = 0;
for (R int i = 1; i <= l; ++i)
{
for (R int j = 0; j < 26; ++j)
if(to[now][j])
{
now = to[now][j];
break;
}
}
printf("%d\n", len[now] - l + 1);
}
}
int main(void)
{
int T;
scanf("%d", &T);
W (T--)
{
scanf("%s", dat + 1); l = std::strlen(dat + 1);
par[last = cnt = 0] = -1; std::memset(to, 0, sizeof(to));
for (R int i = 1; i <= l; ++i) SAM::insert(dat[i] - 'a');
for (R int i = 1; i <= l; ++i) SAM::insert(dat[i] - 'a');
SAM::calc();
}
}