using System;
// you can also use other imports, for example:
// using System.Collections.Generic;
class Solution {
public int solution(string S) {
return Solve(S);
}
private class RabinKarp
{
public long B, M;
public long[] RK, ER;
public RabinKarp(long b, long m) { B = b; M = m; }
public RabinKarp Initialize(int n, int ner) {
RK = new long[n];
ER = new long[ner]; ER[0] = 1;
for (int i = 1; i < ner; ER[i] = (ER[i - 1] * B) % M, i++) ;
return this;
}
public long this[int start, int end] {
get { return (RK[end] - (RK[start-1] * ER[end - start + 1]) % M + M) % M; }
}
public static void BuildRKParams(string s, int start, int end, RabinKarp[] rks) {
foreach (RabinKarp rk in rks)
rk.RK[start] = s[start];
for (int i = start+1; i <= end; i++)
foreach (RabinKarp rk in rks)
rk.RK[i] = ((rk.RK[i - 1] * rk.B) % rk.M + s[i]) % rk.M;
}
}
private class KMP
{
public int[] SkipSearchTable = null;
public KMP(string pattern) {
SkipSearchTable = BuildKMPSkipSearchTable(pattern, pattern.Length);
}
protected int[] BuildKMPSkipSearchTable(string ptn, int n)
{
int[] skipSearchTable = new int[n];
int i = 2, j = 0;
skipSearchTable[0] = -1; skipSearchTable[1] = 0;
while (i < n) {
if (ptn[i - 1] == ptn[j]) skipSearchTable[i++] = ++j;
else if (j > 0) j = skipSearchTable[j];
else skipSearchTable[i++] = 0;
}
return skipSearchTable;
}
public int Search(string s, int ell, int pLength, int start, int end)
{
int i = ell, j = start, k = start;
end = end - pLength + 1;
while (j <= end) {
while (i < pLength && s[i] == s[i + j]) ++i;
if (i >= pLength) {
while (k < ell && s[k] == s[j + k]) ++k;
if (k >= ell) return j;
}
j += (i - SkipSearchTable[i]);
if (i == ell) k = k > 0 ? (k - 1) : 0;
else
if (SkipSearchTable[i] <= ell) {
k = SkipSearchTable[i] >= 0 ? SkipSearchTable[i] : 0;
i = ell;
} else {
k = ell;
i = SkipSearchTable[i];
}
}
return -1;
}
}
public int Solve(string S)
{
if (S.Length < 3) return 0;
int n = S.Length, ell = 1, scanIdx = 0;
for (; ell < n && S[ell - 1] == S[ell]; ell++) ;
if (ell == n) return (n / 3);
if (ell < n / 3) {
KMP kmp = new KMP(S);
scanIdx = (int)Math.Min(n / 3 - 1, kmp.SkipSearchTable[n - 1]);
if (ell <= scanIdx)
{
RabinKarp[] rks = new RabinKarp[] {
new RabinKarp(29, 777333111).Initialize(n, scanIdx + 2),
new RabinKarp(31, 555111333).Initialize(n, scanIdx + 2)
};
RabinKarp.BuildRKParams(S, 0, scanIdx, rks);
RabinKarp.BuildRKParams(S, n - scanIdx - 2, n-1, rks);
for (int j = n - scanIdx - 1, idx = -1; scanIdx >= 0; scanIdx--, j++)
{
if (ell > 1 && ell > scanIdx) break;
bool skip = false;
foreach (RabinKarp rk in rks) {
if (rk.RK[scanIdx] != rk[j, n - 1]) {
skip = true; break;
}
}
if (skip) continue;
idx = kmp.Search(S, (scanIdx == 0 && ell == 1) ? 0 : ell, scanIdx + 1, scanIdx + 1, n - scanIdx - 2);
if (idx < 0) continue;
if (idx == n) break;
if (idx >= 0) return scanIdx + 1;
}
}
}
if (scanIdx < 0) return 0;
int ellRight = n, c0 = S[0];
for (; ellRight > ell && S[ellRight - 1] == c0; ellRight--) ;
if (n - ellRight <= 0) return 0;
int min = (int)Math.Min(ell, n - ellRight);
int ret = (int)Math.Min(min, ((int)Math.Max(ell, n - ellRight)) / 2);
if (ret >= min) return min;
scanIdx = ell; ell = -1;
while (scanIdx < ellRight)
{
if (S[scanIdx] == c0) {
if (ell < 0) ell = scanIdx;
while (++scanIdx < ellRight && S[scanIdx] == c0) ;
continue;
}
if (ell >= 0) {
if (ret < scanIdx - ell) {
ret = scanIdx - ell;
if (ret >= min) return ret;
}
ell = -1;
}
scanIdx++;
}
return (int)Math.Min(min, ell < 0 ? ret : (int)Math.Max(ret, scanIdx - ell));
}
}