这里介绍一种失了智的算法。
这种算法基于插桩原理。我们每隔len插一个庄,我们发现所有长度为len的子串都会和一个桩相交。然后对于每个长度len我们只要能够通过n/lenlog(len)的复杂度算出所有长度为len的SAN值就可以在nloglog的时间复杂度内解决问题。
这里有三个子问题:
1.如何判断[l1,r1]和[l2,r2]这两个子串是否相等,这里采用字符串哈希O(n)预处理 O(单次取模)查询。
2.如何判断过当前桩的所有子串和过前一个桩的子串有递增SAN值的关系,这里采用二分加哈希的方式来解决O(n)预处理 O(log*单次取模)的复杂度,也可以采用两次后缀数组的方式O(nlog)预处理 O(1)查询会更快一些。
3.如何处理长度为len的子串中某些san值递增,某些san值清零的问题。线段树O(log)处理

即使如此,你仍然无法通过此题,因为整体复杂度是O(nloglog*单次取模复杂度),%运算符是O(log)的,但是如今有取模黑科技可以比%运算符更加迅速,如此可以通过此题。

反正只要胆子大,常数卡卡就过了。

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

#define lson rt * 2
#define rson rt * 2 + 1

typedef long long ll;

const int N = 1e5 + 100;
const int MOD = 1e9 + 7;
const int SZ = 200;

typedef  unsigned  long long ull;
typedef __uint128_t L;

struct FastMod {
  ull b, m;
  FastMod(ull b) : b(b), m(ull((L(1) << 64) / b)) {}
  ull reduce(ull a) {
    return a - (ull)((L(m) * a) >> 64) * b;
  }
}F(MOD);

int tree[N * 4], laz[N * 4], flag[N * 4];

void push_down(int rt) {
    if (flag[rt]) {
        tree[lson] = laz[lson] = tree[rson] = laz[rson] = 0;
        flag[lson] = flag[rson] = 1;
        flag[rt] = 0;
    }
    laz[lson] += laz[rt];
    tree[lson] += laz[rt];
    laz[rson] += laz[rt];
    tree[rson] += laz[rt];
    laz[rt] = 0;
}

void insert(int rl, int rr, int l, int r, int rt) {
    if (rl == l && rr == r) {
        tree[rt]++;
        laz[rt]++;
        return;
    }
    push_down(rt);
    int m = (l + r) / 2;
    if (rr <= m) insert(rl, rr, l, m, lson);
    else if (m < rl) insert(rl, rr, m + 1, r, rson);
    else {
        insert(rl, m, l, m, lson);
        insert(m + 1, rr, m + 1, r, rson);
    }
    tree[rt] = max(tree[lson], tree[rson]) + laz[rt];
}

void clr(int rl, int rr, int l, int r, int rt) {
    if (rl == l && rr == r) {
        tree[rt] = laz[rt] = 0;
        flag[rt] = 1;
        return;
    }
    push_down(rt);
    int m = (l + r) / 2;
    if (rr <= m) clr(rl, rr, l, m, lson);
    else if (m < rl) clr(rl, rr, m + 1, r, rson);
    else {
        clr(rl, m, l, m, lson);
        clr(m + 1, rr, m + 1, r, rson);
    }
    tree[rt] = max(tree[lson], tree[rson]) + laz[rt];
}

int n;
char str[N];
ll pre[N], rff[N];

int qpow(int x, int n) {
    int r = 1;
    while (n > 0) {
        if (n & 1) r = 1LL * r * x % MOD;
        n /= 2;
        x = 1LL * x * x % MOD;
    }
    return r;
}

void init() {
    ll t = 1;
    for (int i = 1; i <= n; i++) {
        pre[i] = (pre[i - 1] + t * (str[i] - 'a')) % MOD;
        t = t * 26 % MOD;
    }
    int r = qpow(26, MOD - 2);
    rff[0] = 1;
    for (int i = 1; i <= n; i++) rff[i] = 1LL * rff[i - 1] * r % MOD;
}


/*int get(int l, int r) {
    if (r > n) return -1;
    int ans = 1LL * (pre[r] - pre[l - 1]) * rff[l - 1] % MOD;
    if (ans < 0) ans += MOD;
    return ans;
}*/


int get(int l, int r) {
    if (r > n) return -1; 
    int ans = F.reduce(F.reduce(pre[r] - pre[l - 1] + MOD) * rff[l - 1]);
    if (ans >= MOD) ans -= MOD;
    //printf("%d %d %d\n", l, r, ans);
    return ans;
}


void upd(int &x, int y) {
    if (y > x) x = y;
}

bool check(int l, int r, int d) {
    return get(l, r) == get(l + d, r + d);
}

int q1(int ql, int qr, int len) {
    if (str[ql] != str[ql - len]) return ql - 1;
    int m, l = ql, r = qr;
    while (r - l > 1) {
        m = (l + r) / 2;
        if (check(ql, m, -len)) l = m;
        else r = m;
    }
    if (check(ql, r, -len)) return r;
    return l;
}

int q2(int ql, int qr, int len) {
    if (str[qr] != str[qr - len]) return 1e9;
    int m, l = ql, r = qr;
    while (l < r) {
        m = (l + r) / 2;
        if (check(m, qr, -len)) r = m;
        else l = m + 1;
    }
    return l;
}

int main() {
    //freopen("0.txt", "r", stdin);
    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%s", str + 1);
        n = strlen(str + 1);
        init();
        int ans = 0;
        for (int ma = 1; ma <= SZ && ma <= n; ma++) {
            for (int i = 1; i <= ma; i++) {
                int res = 1;
                for (int j = i; j <= n; j += ma) {
                    if (j - ma >= 1 && get(j - ma, j - 1) == get(j, j + ma - 1)) res++;
                    else {
                        upd(ans, res);
                        res = 1;
                    }
                }
                upd(ans, res);
            }
        }
        for (int sz = SZ + 1; sz <= n; sz++) {
            clr(1, sz, 1, sz, 1);
            for (int i = 2 * sz; i <= n; i += sz) {
                int l = q2(i - sz + 1, i, sz) - i + sz, r = q1(i, i + sz - 1, sz) - i + 1;
                if (r < l) {
                    clr(1, sz, 1, sz, 1);
                }
                else {
                    if (l > 1) clr(1, l - 1, 1, sz, 1);
                    if (r < sz) clr(r + 1, sz, 1, sz, 1);
                    insert(l, r, 1, sz, 1);
                }
                upd(ans, tree[1] + 1);
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}