【YBTOJ】【BZOJ 3620】似乎在梦中见过的样子
题目大意:
已知一个字符串 \(S\),求它有多少个形如 \(A+B+A\) 的子串(\(|A|\geq k,|B|\geq 1\))。位置不同其他性质相同的子串算不同子串,位置相同但拆分不同的子串算同一子串。
\(|S|\leq1.5\times10^4,k\leq100\)。
正文:
本题暴力枚举左端点然后 KMP,由于 \(\mathcal{O}(n)\) 有点吃力,所以需要卡卡常。
代码:
const int N = 1e6 + 10;
inline ll READ()
{
ll x = 0, f = 1;
char c = getchar();
while (c != '-' && (c < '0' || c > '9')) c = getchar();
if (c == '-') f = -f, c = getchar();
while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
return x * f;
}
char s[N];
int nxt[N], ans;
int n, k;
inline void KMP (char *s, int n, int op = 1)
{
int j = 0;
for (int i = op; i < n; i++)
{
while (j && s[i] != s[j]) j = nxt[j];
if (s[i] == s[j]) j++;
if(op) nxt[i + 1] = j;
else
{
if (2 * j > i) j = nxt[j];
if (j >= k) ans ++;
}
}
return;
}
int main()
{
scanf("%s", s);
k = READ();
n = strlen(s);
for (int i = 1; i < n; i++)
{
KMP (s + i - 1, n - i + 1);
KMP (s + i - 1, n - i + 1, 0);
}
printf ("%d\n", ans);
return 0;
}