ACM模板

本文深入探讨了算法与数据结构的关键概念,包括动态规划、背包问题、多重背包、数论、线段树、FFT、NTT、字典树、AC自动机、后缀数组、回文树和后缀自动机等高级主题。覆盖了从基本原理到实践应用的广泛内容,为读者提供了全面的理解。

动态规划

背包

01背包

//物品数量为n 背包容量为v
//第i个物品的价值为val[i] 体积为vol[i]
//最终结果为dp[v]
const int maxn = 1010;
int dp[maxn];
int vol[maxn];
int val[maxn];
int n, v;

void solve() {
    memset(dp, 0, sizeof(dp));
    for(int i = 0; i < n; i++) {
        for(int j = v; j >= vol[i]; j--) {
            dp[j] = max(dp[j], dp[j - vol[i]] + val[i]);
        }
    }
}

完全背包

const int maxn = 1010;
int dp[maxn];
int vol[maxn];
int val[maxn];
int n, v;

void solve() {
    memset(dp, 0, sizeof(dp));
    for(int i = 0; i < n; i++) {
        for(int j = vol[i]; j <= v; j++) {
            dp[j] = max(dp[j], dp[j - vol[i]] + val[i]);
        }
    }
}

多重背包

//恰好装满 hdu2844
#include<iostream>
#include<algorithm>
#include<climits>
#include<cstring>
#include<cstdio>
using namespace std;

const int maxn = 110;
const int maxm = 100010;
int val[maxn];
int num[maxn];
int dp[maxm];
int n, m;

void solve() {
    memset(dp, -1, sizeof(dp));
    dp[0] = 0;
    for(int i = 0; i < n; i++) {
        if(val[i] * num[i] >= m) {
            for(int j = val[i]; j <= m; j++) {
                if(dp[j - val[i]] != -1) {
                    dp[j] = max(dp[j], dp[j - val[i]] + val[i]);
                }
            }
        }
        else {
            int cnt = 1;
            int totVal = val[i];
            while(num[i] > cnt) {
                for(int j = m; j >= totVal; j--) {
                    if(dp[j - totVal] != -1) {
                        dp[j] = max(dp[j], dp[j - totVal] + totVal);
                    }
                }
                num[i] -= cnt;
                cnt <<= 1;
                totVal <<= 1;
            }
            totVal = num[i] * val[i];
            for(int j = m; j >= totVal; j--) {
                if(dp[j - totVal] != -1) {
                    dp[j] = max(dp[j], dp[j - totVal] + totVal);
                }
            }
        }
    }
    int ans = 0;
    for(int i = 1; i <= m; i++) {
        if(dp[i] == i) {
            ans++;
        }
    }
    printf("%d\n", ans);
}

int main() {
    while(scanf("%d%d", &n, &m) && (n || m)) {
        for(int i = 0; i < n; i++) {
            scanf("%d", val + i);
        }
        for(int i = 0; i < n; i++) {
            scanf("%d", num + i);
        }
        solve();
    }
    return 0;
}

多重背包判断可行性

//每种有若干件的物品能否填满给定容量的背包
//O(NV)
// hdu2844
#include<iostream>
#include<algorithm>
#include<climits>
#include<cstring>
#include<cstdio>
using namespace std;

const int maxn = 101;
const int maxm = 100001;
int val[maxn];
int num[maxn];
int dp[maxm];
int n, m;

void solve() {
    for(int i = 1; i <= m; i++) {
        dp[i] = -1;
    }
    dp[0] = 0;
    for(int i = 1; i <= n; i++) {
        for(int j = 0; j <= m; j++) {
            dp[j] = dp[j] >= 0 ? num[i] : -1;
        }
        for(int j = 0; j <= m - val[i]; j++) {
            if(dp[j] > 0) {
                dp[j + val[i]] = max(dp[j + val[i]], dp[j] - 1);
            }
        }
    }
    int ans = 0;
    for(int i = 1; i <= m; i++) {
        if(dp[i] != -1) {
            ans++;
        }
    }
    printf("%d\n", ans);
}

int main() {
    while(scanf("%d%d", &n, &m) && (n || m)) {
        for(int i = 1; i <= n; i++) {
            scanf("%d", val + i);
        }
        for(int i = 1; i <= n; i++) {
            scanf("%d", num + i);
        }
        solve();
    }
    return 0;
}

数论

组合数求模

卢卡斯定理

//FZU 2020
//要求p为素数
//适合使用的场景:
//1 <= m <= n <= 1e18 , p <= 1e5
typedef long long LL;
LL quickPowMod(LL n, LL r, LL mod) {
    LL res = 1;
    n %= mod;
    while(r) {
        if(r & 1) {
            res = (res * n) % mod;
        }
        n = (n * n) % mod;
        r >>= 1;
    }
    return res;
}

LL inverse(LL n, LL mod) {
    return quickPowMod(n, mod - 2, mod);
}

LL C(LL n, LL m, LL mod) {
    if(m > n) {
        return 0;
    }
    LL res = 1;
    LL a, b;
    for(int i = 1; i <= m; i++) {
        a = (n + i - m) % mod;
        b = inverse(i, mod);
        res = (((res * a) % mod)* b) % mod;
    }
    return res;
}

LL lucas(LL n, LL m, LL mod) {
    if(m == 0) {
        return 1;
    }
    return C(n % mod, m % mod, mod) * lucas(n / mod, m / mod, mod) % mod;
}

int main() {
    int t;
    LL n, m, mod;
    cin >> t;
    while(t--) {
        cin >> n >> m >> mod;
        cout << lucas(n, m, mod) << endl;
    }
    return 0;
}

扩展卢卡斯定理

Cnm%p&ThickSpace;&ThickSpace;1≤m≤n≤1018&ThickSpace;&ThickSpace;2≤p≤1000000&ThickSpace;&ThickSpace;且p不保证为质数C_n^m \% p\;\;1\leq m \leq n \leq 10^{18}\;\;2\leq p \leq 1000000\;\;且p不保证为质数Cnm%p1mn10182p1000000p

//洛谷4720模板
//Gym - 100633J
#include<iostream>
#include<algorithm>
#include<set>
#include<vector>
#include<map>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;

LL quickPowerMod(LL n, LL r, LL p) {
    LL res = 1;
    n %= p;
    while(r) {
        if(r & 1) {
            res = res * n % p;
        }
        r >>= 1;
        n = n * n % p;
    }
    return res;
}

LL exgcd(LL a, LL b, LL &x, LL &y) {
    if(b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    LL res = exgcd(b, a % b, x, y);
    LL temp = x;
    x = y;
    y = temp - a / b * y;
    return res;
}

LL inverse(LL a, LL m) {
    LL x1, x2;
    LL d = exgcd(a, m, x1, x2);
    if(d != 1) {
        return -1;
    }
    return (x1 % m + m) % m;
}

LL CRT(vector<LL> &b, vector<LL> &m) {
    LL n = m.size();
    LL ans = 0;
    LL M = 1;
    LL Mi;
    LL x, y;
    for(LL temp : m) {
        M *= temp;
    }
    for(LL i = 0; i < n; i++) {
        Mi = M / m[i];
        //exgcd(Mi, m[i], x, y);
        ans = (ans + b[i] * Mi * inverse(Mi, m[i])) % M;
    }
    ans = (ans + M) % M;
    return ans;
}

//计算n!中因子p的个数
LL calculateExp(LL n, LL p) {
    LL res = 0;
    while(n) {
        res += n / p;
        n /= p;
    }
    return res;
}

//计算n!中除去所有因子p之后模mod(p^x)的值
LL facModP(LL n, LL mod, LL p) {
    if(n == 0) {
        return 1;
    }
    LL res = 1;
    if(n / mod) {
        for(LL i = 2; i <= mod; i++) {
            if(i % p != 0) {
                res = res * i % mod;
            }
        }
        res = quickPowerMod(res, n / mod, mod);
    }
    LL temp = n % mod;
    for(LL i = 2; i <= temp; i++) {
        if(i % p != 0) {
            res = res * i % mod;
        }
    }
    return res * facModP(n / p, mod, p) % mod;
}

//计算C(n, m) % p^x 的值
LL CmodP(LL n, LL m, LL mod, LL p) {
    LL res, inv1, inv2;
    LL cnt = 0;
    cnt += calculateExp(n, p);
    cnt -= calculateExp(m, p);
    cnt -= calculateExp(n - m, p);

    res = facModP(n, mod, p);
    inv1 = facModP(m, mod, p);
    inv2 = facModP(n - m, mod, p);
    inv1 = inverse(inv1, mod);
    inv2 = inverse(inv2, mod);
    res = (res * inv1 % mod) * inv2 % mod;
    res = res * quickPowerMod(p, cnt, mod) % mod;
    return res;
}

//计算C(n, m) % mod(合数)的值
LL CmodX(LL n, LL m, LL mod) {
    vector<LL> b, md;
    LL temp;
    for(LL i = 2; i * i <= mod; i++) {
        if(mod % i == 0) {
            temp = 1;
            while(mod % i == 0) {
                temp *= i;
                mod /= i;
            }
            md.push_back(temp);
            b.push_back(CmodP(n, m, temp, i));
        }
    }
    if(mod > 1) {
        md.push_back(mod);
        b.push_back(CmodP(n, m, mod, mod));
    }
    return CRT(b, md);
}

int main() {
    LL n, m, p;
    while(cin >> n >> m >> p) {
        cout << CmodX(n, m, p) << endl;
    }
    return 0;
}

模线性方程组

中国剩余定理

typedef long long LL;
const int maxn = 1000;
LL b[maxn];
LL m[maxn];

LL extendGcd(LL a, LL b, LL &x, LL &y) {
    if(b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    LL res = extendGcd(b, a % b, x, y);
    LL temp = x;
    x = y;
    y = temp - a/b*y;
    return res;
}

LL CRT(LL *b, LL *m, int n) {
    LL ans = 0;
    LL M = 1;
    LL Mi;
    LL x, y;
    for(int i = 0; i < n; i++) {
        M *= m[i];
    }
    for(int i = 0; i < n; i++) {
        Mi = M / m[i];
        extendGcd(Mi, m[i], x, y);
        ans = (ans + b[i] * Mi * x) % M;
    }
    ans = (ans + M) % M;
    return ans;
}

扩展中国剩余定理

//模板验证POJ2891

typedef long long LL;
const int maxn = 1000;
LL b[maxn];
LL m[maxn];

LL exGcd(LL a, LL b, LL &x, LL &y) {
    if(b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    LL res = exGcd(b, a % b, x, y);
    LL temp = x;
    x = y;
    y = temp - a/b*y;
    return res;
}

LL gcd(LL a, LL b) {
    return b == 0 ? a : gcd(b, a % b);
}

LL inverse(LL a, LL m) {
    LL x1, x2;
    LL d = exGcd(a, m, x1, x2);
    if(d != 1) {
        return -1;
    }
    return x1 % m;
}

bool merge(LL m1, LL b1, LL m2, LL b2, LL &m3, LL &b3) {
    LL d = gcd(m1, m2);
    LL b = b2 - b1;
    if(b % d) {
        return false;
    }
    b = (b % m2 + m2) % m2;
    m1 /= d;
    m2 /= d;
    b /= d;
    b *= inverse(m1, m2);
    b %= m2;
    b *= m1 * d;
    b += b1;
    m3 = m1 * m2 * d;
    b3 = (b % m3 + m3) % m3;
    return true;
}

LL CRT(LL *b, LL *m, int n) {
    LL tempm, tempb;
    for(int i = 0; i < n - 1; i++) {
        if(merge(m[i], b[i], m[i + 1], b[i + 1], tempm, tempb)) {
            m[i + 1] = tempm;
            b[i + 1] = tempb;
        }
        else {
            return -1;
        }
    }
    return (b[n - 1] % m[n - 1] + m[n - 1]) % m[n - 1];
}

int main() {
    int n;
    while(cin >> n) {
        for(int i = 0; i < n; i++) {
            cin >> m[i] >> b[i];
        }
        cout << CRT(b, m, n) << endl;
    }
    return 0;
}

欧拉筛

欧拉筛筛选素数

const int maxn = 100000;
bool isPrim[maxn];
int primes[maxn];
int countPrim;
void sieve() {
    int temp;
    memset(isPrim, -1, sizeof(isPrim));
    isPrim[1] = false;
    countPrim = 0;
    for(int i = 2; i < maxn; i++) {
        if(isPrim[i]) {
            primes[countPrim++] = i;
        }
        for(int j = 0; j < countPrim && (temp = i * primes[j]) < maxn; j++) {
            isPrim[temp] = false;
            if(i % primes[j] == 0) {
                break;
            }
        }
    }
}

欧拉筛计算欧拉函数

const int maxn = 100000;
bool isPrim[maxn];
int primes[maxn];
int phi[maxn];
int countPrim;
void sieve() {
    LL temp;
    memset(isPrim, -1, sizeof(isPrim));
    memset(phi, 0, sizeof(phi));
    phi[1] = 1;
    countPrim = 0;
    for(int i = 2; i < maxn; i++) {
        if(isPrim[i]) {
            primes[countPrim++] = i;
            phi[i] = i - 1;
        }
        for(int j = 0; j < countPrim && (temp = i * primes[j]) < maxn; j++) {
            isPrim[temp] = false;
            if(i % primes[j] == 0) {
                phi[temp] = phi[i]*primes[j];
                break;
            }
            else {
                phi[temp] = phi[i]*phi[primes[j]];
            }
        }
    }
}

欧拉筛计算莫比乌斯函数

const int maxn = 100000;
bool isPrim[maxn];
int primes[maxn];
int mu[maxn];
int countPrm;
void sieve() {
    memset(isPrim, -1, sizeof(isPrim));
    memset(mu, 0, sizeof(mu));
    memset(primes, 0, sizeof(primes));
    countPrim = 0;
    isPrim[1] = false;
    mu[1] = 1;
    LL temp;
    for(int i = 2; i < maxn; i++) {
        if(isPrim[i]) {
            primes[countPrim++] = i;
            mu[i] = -1;
        }
        for(int j = 0; j < countPrim && (temp = i * primes[j]) < maxn; j++) {
            isPrim[temp] = false;
            if(i % primes[j] == 0) {
                mu[temp] = 0;
                break;
            }
            else {
                mu[temp] = -mu[i];
            }
        }
    }
}

Gauss-Jordan消元

const double eps = 1e-8;
const int maxn = 110;
typedef double Matrix[maxn][maxn];

void guassJordan(Matrix A, int n) {
    int i, j, k, r;
    for(i = 0; i < n; i++) {
        r = i;
        for(j = i + 1; j < n; j++) {
            if(fabs(A[j][i]) > fabs(A[r][i])) {
                r = j;
            }
        }
        if(fabs(A[r][i]) < eps) {
            continue;
        }
        if(r != i) {
            for(j = 0; j <= n; j++) {
                swap(A[r][j], A[i][j]);
            }
        }
        for(k = 0; k < n; k++) {
            if(k != i) {
                for(j = n; j >= i; j--) {
                    A[k][j] -= A[k][i] / A[i][i] * A[i][j];
                }
            }
        }
    }
}

FFT NTT FWT

FFT

//模板验证 HDU1402 模拟大数乘法
#include<iostream>
#include<algorithm>
#include<complex>
#include<cstdio>
#include<cstring>
#include<cstring>
using namespace std;

const int maxm = 17;
const int maxn = 1 << maxm;
complex<double> buffer1[maxn];
complex<double> buffer2[maxn];
complex<double> omega[maxn];
complex<double> conjOmega[maxn];
char number1[maxn];
char number2[maxn];
int sum[maxn];

void initOmega(int n) {
    double pi = acos(-1);
    for(int i = 0; i < n; i++) {
        omega[i] = complex<double>(cos(2 * pi * i / n), sin(2 * pi * i / n));
        conjOmega[i] = conj(omega[i]);
    }
}

void inverse(int n, complex<double>* buffer) {
    for(int i = 0, j = 0; i < n; i++) {
        if(i < j) {
            swap(buffer[i], buffer[j]);
        }
        for(int k = n >> 1; (j ^= k) < k; k >>= 1) {
            continue;
        }
    }
}

void fft(int n, complex<double>* buffer, complex<double>* omega) {
    inverse(n, buffer);
    for(int i = 2; i <= n; i <<= 1) {
        int m = i >> 1;
        int step = n / i;
        for(int j = 0; j < n; j += i) {
            for(int k = 0; k < m; k++) {
                complex<double> temp = buffer[j + k];
                buffer[j + k] = buffer[j + k] + omega[step * k] * buffer[j + k + m];
                buffer[j + k + m] = temp - omega[step * k] * buffer[j + k + m];
            }
        }
    }
}

int main() {
    int len1, len2, len;
    while(scanf("%s%s", number1, number2) == 2) {
        len1 = strlen(number1);
        len2 = strlen(number2);
        for(len = 1; len < len1 * 2 || len < len2 * 2; len <<= 1) {
            continue;
        }
        for(int i = 0; i < len1; i++) {
            buffer1[i] = number1[len1 - i - 1] - '0';
        }
        for(int i = len1; i < len; i++) {
            buffer1[i] = 0;
        }
        for(int i = 0; i < len2; i++) {
            buffer2[i] = number2[len2 - i - 1] - '0';
        }
        for(int i = len2; i < len; i++) {
            buffer2[i] = 0;
        }
        initOmega(len);
        fft(len, buffer1, omega);
        fft(len, buffer2, omega);
        for(int i = 0; i < len; i++) {
            buffer1[i] *= buffer2[i];
        }
        fft(len, buffer1, conjOmega);
        for(int i = 0; i < len; i++) {
            sum[i] = (int)(buffer1[i].real() / len + 0.5);
        }
        for(int i = 0; i < len; i++) {
            sum[i + 1] += sum[i] / 10;
            sum[i] %= 10;
        }
        for(len = len1 + len2 - 1; len > 0 && sum[len] <= 0; len--) {
            continue;
        }
        for(int i = len; i >= 0; i--) {
            printf("%d", sum[i]);
        }
        putchar('\n');
    }
    return 0;
}

NTT

//模板验证 HDU1402 模拟大数乘法
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cstring>
using namespace std;
typedef long long LL;

const LL MOD = 1004535809;
const LL gOfMod = 3;
const LL maxm = 17;
const LL maxn = 1 << maxm;
LL g[maxn];
LL invG[maxn];
LL buffer1[maxn];
LL buffer2[maxn];
char number1[maxn];
char number2[maxn];
LL sum[maxn];

LL quickPowerMod(LL n, LL r, LL mod) {
    LL res = 1;
    n %= mod;
    while(r) {
        if(r & 1) {
            res = res * n % mod;
        }
        n = n * n % mod;
        r >>= 1;
    }
    return res;
}

void initG(LL n) {
    g[0] = 1;
    g[1] = quickPowerMod(3, (MOD - 1) / n, MOD);
    invG[0] = 1;
    invG[1] = quickPowerMod(g[1], MOD - 2, MOD);
    for(LL i = 2; i < n; i++) {
        g[i] = g[i - 1] * g[1] % MOD;
        invG[i] = invG[i - 1] * invG[1] % MOD;
    }
}

void inverseBuffer(LL n, LL* buffer) {
    for(LL i = 0, j = 0; i < n; i++) {
        if(i > j) {
            swap(buffer[i], buffer[j]);
        }
        for(int k = n >> 1; (j ^= k) < k; k >>= 1) {
            continue;
        }
    }
}

void fft(LL n, LL* buffer, LL* g) {
    inverseBuffer(n, buffer);
    for(LL i = 2; i <= n; i <<= 1) {
        LL m = i / 2;
        LL step = n / i;
        for(int j = 0; j < n; j+= i) {
            for(int k = 0; k < m; k++) {
                LL temp = buffer[j + k];
                buffer[j + k] = (buffer[j + k] + g[k * step] * buffer[j + k + m] % MOD) % MOD;
                buffer[j + k + m] = ((temp - g[k * step] * buffer[j + k + m]) % MOD + MOD) % MOD;
            }
        }
    }
}

int main() {
    LL len1, len2, len;
    while(scanf("%s%s", number1, number2) == 2) {
        len1 = strlen(number1);
        len2 = strlen(number2);
        for(len = 1; len < len1 * 2 || len < len2 * 2; len <<= 1) {
            continue;
        }
        for(int i = 0; i < len1; i++) {
            buffer1[i] = number1[len1 - i - 1] - '0';
        }
        for(int i = len1; i < len; i++) {
            buffer1[i] = 0;
        }
        for(int i = 0; i < len2; i++) {
            buffer2[i] = number2[len2 - i - 1] - '0';
        }
        for(int i = len2; i < len; i++) {
            buffer2[i] = 0;
        }
        initG(len);
        fft(len, buffer1, g);
        fft(len, buffer2, g);
        for(int i = 0; i < len; i++) {
            buffer1[i] = buffer1[i] * buffer2[i] % MOD;
        }
        fft(len, buffer1, invG);
        LL invLen = quickPowerMod(len, MOD - 2, MOD);
        for(int i = 0; i < len; i++) {
            sum[i] = buffer1[i] * invLen % MOD;
        }
        for(int i = 0; i < len; i++) {
            sum[i + 1] += sum[i] / 10;
            sum[i] %= 10;
        }
        for(len = len1 + len2 - 1; len > 0 && sum[len] <= 0; len--) {
            continue;
        }
        while(len >= 0) {
            printf("%I64d", sum[len--]);
        }
        putchar('\n');
    }
    return 0;
}

数据结构

线段树

线段树单点更新

非递归建树
/*
模板验证 HDU1166
求区间和,题目给的区间描述是左闭右闭的,代码中全部使用左开右闭的区间
所有下标从0开始
非递归建树建立的是一颗满二叉树
*/
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>

#define Add "Add"
#define Sub "Sub"
#define End "End"
#define Query "Query"

using namespace std;

const int maxn = 50050;
const int inf = 0x7fffffff;
int date[maxn * 4];
int leaves;	//叶子节点数量
int n;
char order[6];

void init() {
    leaves = 1;
    while(leaves < n) {
        leaves <<= 1;
    }
    memset(date, 0, sizeof(date));
}

void update(int index, int val) {
    index += leaves - 1;       //原数组中下标在线段树对应的数组中下标值的计算方法
    date[index] += val;
    while(index > 0) {
        index = (index - 1) / 2;
        date[index] += val;
    }
}

//要求查询的区间为[a, b), 当前查询的区间为[l, r),当前区间对应的节点为k
//在主函数中调用时调用方法为query(a, b, 0, 0, leaves);
int query(int a, int b, int k, int l, int r) {
    if(a >= r || b <= l) {
        return 0;
    }
    if(a <= l && b >= r) {
        return date[k];
    }
    return query(a, b, 2 * k + 1, l, (l + r)/2) + query(a, b, 2 * k + 2, (l + r) / 2, r);
}

int main() {
    int t, temp, index, l ,r;
    scanf("%d", &t);
    for(int cs = 1; cs <= t; cs++) {
        scanf("%d", &n);
        init();
        for(int i = 0; i < n; i++) {
            scanf("%d", &temp);
            update(i, temp);
        }
        printf("Case %d:\n", cs);
        while(scanf("%s", order)) {
            if(strcmp(order, End) == 0) {
                break;
            }
            else if(strcmp(order, Add) == 0) {
                scanf("%d%d", &index, &temp);
                update(index - 1, temp);
            }
            else if(strcmp(order, Sub) == 0) {
                scanf("%d%d", &index, &temp);
                update(index - 1, -temp);
            }
            else if(strcmp(order, Query) == 0) {
                scanf("%d%d", &l, &r);
                printf("%d\n", query(l - 1, r, 0, 0, leaves));
            }
        }
    }
    return 0;
}

递归建树
/*
模板验证 HDU1166
求区间和,题目给的区间描述是左闭右闭的,代码中全部使用左开右闭的区间
所有下标从0开始
*/
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>

#define Add "Add"
#define Sub "Sub"
#define End "End"
#define Query "Query"

using namespace std;

const int maxn = 50050;
const int inf = 0x7fffffff;
int date[maxn * 4];
int sourceDate[maxn];
int n;
char order[6];

//当前节点为v,对应区间为[l, r)
void build(int v, int l, int r) {
    if(r - l == 1) {
        date[v] = sourceDate[l];
    }
    else {
        build(2 * v + 1, l, (l + r) / 2);
        build(2 * v + 2, (l + r) / 2, r);
        date[v] = date[2 * v + 1] + date[2 * v + 2];
    }
}

//需要修改原数组中下标为index的元素,变化的值为val
void update(int index, int val, int v, int l, int r) {
    if(r - l == 1) {
        date[v] += val;
    }
    else {
        if(index < (l + r) / 2) {
            update(index, val, v * 2 + 1, l, (l + r) / 2);
        }
        else {
            update(index, val, v * 2 + 2, (l + r) / 2, r);
        }
        date[v] = date[2 * v + 1] + date[2 * v + 2];
    }
}

int query(int a, int b, int v, int l, int r) {
    if(a >= r || b <= l) {
        return 0;
    }
    if(a <= l && b >= r) {
        return date[v];
    }
    return query(a, b, 2 * v + 1, l, (l + r) / 2) + query(a, b, 2 * v + 2, (l + r) / 2, r);
}

int main() {
    int t, index, val, l, r;
    scanf("%d", &t);
    for(int cs = 1; cs <= t; cs++) {
        scanf("%d", &n);
        for(int i = 0; i < n; i++) {
            scanf("%d", sourceDate + i);
        }
        build(0, 0, n);
        printf("Case %d:\n", cs);
        while(scanf("%s", order) == 1) {
            if(strcmp(order, End) == 0) {
                break;
            }
            else if(strcmp(order, Add) == 0) {
                scanf("%d%d", &index, &val);
                update(index - 1, val, 0, 0, n);
            }
            else if(strcmp(order, Sub) == 0) {
                scanf("%d%d", &index, &val);
                update(index - 1, -val, 0, 0, n);
            }
            else if(strcmp(order, Query) == 0) {
                scanf("%d%d", &l, &r);
                printf("%d\n", query(l - 1, r, 0, 0, n));
            }
        }
    }
    return 0;
}

线段树区间更新

/*
模板验证POJ3468
计算区间和 
*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;

const int maxn = 100010;
LL sourceDate[maxn];	//原始数据
LL date[maxn << 2];	//存储线段树
LL mark[maxn << 2];	//懒惰标记

void build(int v, int l, int r) {
    mark[v] = 0;
    if(r - l == 1) {
        date[v] = sourceDate[l];
    }
    else {
        build(2 * v + 1, l, (l + r) / 2);
        build(2 * v + 2, (l + r) / 2, r);
        date[v] = date[2 * v + 1] + date[2 * v + 2];
    }
}

//下移懒惰标记
void pushDown(int v, int l, int r) {
    if(mark[v] != 0) {
        int chl = 2 * v + 1;
        int chr = 2 * v + 2;
        int mid = (l + r) / 2;
        mark[chl] += mark[v];
        mark[chr] += mark[v];
        date[chl] += (mid - l) * mark[v];
        date[chr] += (r - mid) * mark[v];
        mark[v] = 0;
    }
}

void update(int a, int b, int val, int v, int l, int r) {
    if(a >= r || b <= l) {
        return ;
    }
    else if(a <= l && b >= r) {
        mark[v] += val;
        date[v] += val * (r - l);
    }
    else {
        pushDown(v, l, r);
        update(a, b, val, 2 * v + 1, l, (l + r) / 2);
        update(a, b, val, 2 * v + 2, (l + r) / 2, r);
        date[v] = date[2 * v + 1] + date[2 * v + 2];
    }
}

LL query(int a, int b, int v, int l, int r) {
    if(a >= r || b <= l) {
        return 0;
    }
    else if(a <= l && b >= r) {
        return date[v];
    }
    else {
        pushDown(v, l, r);
        return query(a, b, 2 * v + 1, l, (l + r) / 2) + query(a, b, 2 * v + 2, (l + r) / 2, r);
    }
}

int main() {
    int n, m;
    char order;
    int a, b, v;
    while(scanf("%d%d", &n, &m) == 2) {
        for(int i = 0; i < n; i++) {
            scanf("%lld", sourceDate + i);
        }
        build(0, 0, n);
        for(int i = 0; i < m; i++) {
            scanf("\n%c", &order);
            if(order == 'Q') {
                scanf("%d%d", &a, &b);
                printf("%lld\n", query(a- 1, b, 0, 0, n));
            }
            else {
                scanf("%d%d%d", &a, &b, &v);
                update(a - 1, b, v, 0, 0, n);
            }
        }
    }
    return 0;
}

线段树+扫描线

HDU1542
//线段树 + 扫描线 + 离散化
//注意这里的区间是闭区间
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
struct Edge {
    double xl;
    double xr;
    double h;
    int flag;
    bool operator<(const Edge &e) {
        return h < e.h;
    }
};

const int maxn = 110;
double len[maxn << 3];
int cover[maxn << 3];
Edge edge[maxn << 1];
double point[maxn << 1];

void build() {
    memset(len, 0, sizeof(len));
    memset(cover, 0, sizeof(cover));
}

void update(int a, int b, int val, int v, int l, int r) {
    if(a >= r || b <= l) {
        return ;
    }
    else if(a <= l && b >= r) {
        cover[v] += val;
        if(cover[v]) {
            len[v] = point[r] - point[l];
        }
        else if(r - l > 1) {
            len[v] = len[2 * v + 1] + len[2 * v + 2];
        }
        else {
            len[v] = 0;
        }
    }
    else {
        update(a, b, val, 2 * v + 1, l, (l + r) / 2);
        update(a, b, val, 2 * v + 2, (l + r) / 2, r);
        if(cover[v]) {
            len[v] = point[r] - point[l];
        }
        else {
            len[v] = len[2 * v + 1] + len[2 * v + 2];
        }
    }
}

int main() {
    int n;
    double x1, x2, y1, y2;
    int cnt;
    int cs = 0;
    double ans;
    int a, b;
    while(scanf("%d", &n) && n) {
        for(int i = 0; i < n; i++) {
            scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);

            edge[2 * i].xl = edge[2 * i + 1].xl = x1;
            edge[2 * i].xr = edge[2 * i + 1].xr = x2;
            edge[2 * i].flag = 1;
            edge[2 * i + 1].flag = -1;
            edge[2 * i].h = y1;
            edge[2 * i + 1].h = y2;

            point[2 * i] = x1;
            point[2 * i + 1] = x2;
        }
        sort(edge, edge + 2 * n);
        sort(point, point + 2 * n);
        cnt = 0;
        for(int i = 1; i < 2 * n; i++) {
            if(point[i] != point[cnt]) {
                point[++cnt] = point[i];
            }
        }
        build();
        ans = 0.0;
        for(int i = 0; i < 2 * n - 1; i++) {
            a = lower_bound(point, point + cnt + 1, edge[i].xl) - point;
            b = lower_bound(point, point + cnt + 1, edge[i].xr) - point;
            update(a, b, edge[i].flag, 0, 0, cnt);
            ans += len[0] * (edge[i + 1].h - edge[i].h);
            //printf("%f %f\n", len[0], edge[i + 1].h - edge[i].h);
        }
        printf("Test case #%d\nTotal explored area: %.2f\n\n", ++cs, ans);
    }
    return 0;
}

可持久化线段树 无更新

//模板验证 HDU2665
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;

struct Node {
    int ls;
    int rs;
    int val;
};

const int maxn = 100010;
Node date[maxn * 20];
int roots[maxn];
int sourceDate[maxn];
int sortDate[maxn];
int cntNode;
int sz;

void build(int &v, int l, int r) {
    v = cntNode++;
    date[v].val = 0;
    if(r - l == 1) {
        return ;
    }
    build(date[v].ls, l, (l + r) / 2);
    build(date[v].rs, (l + r) / 2, r);
}

void update(int pre, int &now, int l, int r, int val) {
    now = cntNode++;
    int mid = (l + r) / 2;
    date[now].ls = date[pre].ls;
    date[now].rs = date[pre].rs;
    date[now].val = date[pre].val + 1;
    if(r - l == 1) {
        return ;
    }
    if(val < mid) {
        update(date[pre].ls, date[now].ls, l, mid, val);
    }
    else {
        update(date[pre].rs, date[now].rs, mid, r, val);
    }
}

int query(int k, int lv, int rv, int l, int r) {
    if(r - l == 1) {
        return l;
    }
    int mid = (l + r) / 2;
    int res = date[date[rv].ls].val - date[date[lv].ls].val;
    if(k <= res) {
        return query(k, date[lv].ls, date[rv].ls, l, mid);
    }
    else {
        return query(k - res, date[lv].rs, date[rv].rs, mid, r);
    }
}

int main() {
    int t;
    int n, m;
    int l, r, k;
    int ans;
    int val;
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &m);
        for(int i = 0; i < n; i++) {
            scanf("%d", sourceDate + i);
            sortDate[i] = sourceDate[i];
        }
        sort(sortDate, sortDate + n);
        sz = unique(sortDate, sortDate + n) - sortDate;
        cntNode = 0;
        build(roots[0], 0, sz);
        for(int i = 0; i < n; i++) {
            val = lower_bound(sortDate, sortDate + sz, sourceDate[i]) - sortDate;
            update(roots[i], roots[i + 1], 0, sz, val);
        }
        for(int i = 0; i < m; i++) {
            scanf("%d%d%d", &l, &r, &k);
            ans = query(k, roots[l - 1], roots[r], 0, sz);
            printf("%d\n", sortDate[ans]);
        }
    }
    return 0;
}

可持久化线段树 单点更新

//ZOJ2112
#include<iostream>
#include<algorithm>
#include<vector>
#include<cstdio>
#include<cstring>
using namespace std;

struct Node {
    int ls;
    int rs;
    int val;
};

struct Operation {
    char order;
    int l;
    int r;
    int k;
};

const int maxn = 50050;
const int maxm = 10010;
Node date[(maxn + maxm) * 32];  //主席树上的节点
Operation op[maxm];             //存储操作
int roots[maxn];                //主席树各个版本的根节点
int bits[maxn];                 //使用树状数组维护的各个主席树的根节点
int sourceDate[maxn + maxm];    //原始序列
int sortDate[maxn + maxm];      //离散化之后的序列
vector<int> li, ri;             //查询和修改时需要访问的主席树(使用树状数组维护的)的下标
char order[3];                  //暂时存储输入的命令'Q'和'C'
int cntNode;                    //统计所有的主席树的节点数量
int sz;                         //保存sortDate数组的大小
int n, m;

void build(int &v, int l, int r) {  //节点date[v]维护的区间为[l, r)
    v = cntNode++;
    date[v].val = 0;
    if(r - l == 1) {
        return ;
    }
    build(date[v].ls, l, (l + r) / 2);
    build(date[v].rs, (l + r) / 2, r);
}

void update(int pre, int &now, int l, int r, int k, int val) {
//now 是当前建立的节点,pre是前一棵树上与now对应的节点,date[now]维护的区间是[l, r)
//当前插入的值在离散化区间中对应的数字为k
//如果是向序列中插入k,则val = 1
//如果是将k替换为其它数字,则val = -1
    now = cntNode++;
    date[now].ls = date[pre].ls;
    date[now].rs = date[pre].rs;
    date[now].val = date[pre].val + val;
    if(r - l == 1) {
        return ;
    }
    int mid = (l + r) / 2;
    if(k < mid) {
        update(date[pre].ls, date[now].ls, l, mid, k, val);
    }
    else {
        update(date[pre].rs, date[now].rs, mid, r, k, val);
    }
}

void add(int index, int val) {
//当有修改操作时需要调用该函数
//index指的是被修改的数字在原序列中的下标(从1开始计数)
//我的下标是从0开始的,所以在计算离散化后它对应的数字的时候,使用index - 1
//如果当前的sourceDate[index - 1]是还没修改的数字,则val = -1;否则val = 1
    int k = lower_bound(sortDate, sortDate + sz, sourceDate[index - 1]) - sortDate;
    while(index <= n) {
        update(bits[index], bits[index], 0, sz, k, val);
        index += index & -index;
    }
}

int query(int sv, int ev, int l, int r, int k) {
//若查询的区间为[s + 1, e],则在组函数中调用该函数时
//sv 为第s棵线段树的根节点下标
//ev 为第e棵线段树的根节点下标(从0开始计数)
//sv和ev所维护的区间为[l, r)
//函数询问的是区间[l, r)中第k小的数(从1开始计数)
    if(r - l == 1) {
        return l;
    }
    int res = 0;

//下面两个循环采用了树状数组的思维进行求和
    for(int i : ri) {
        res += date[date[i].ls].val;
    }
    for(int i : li) {
        res -= date[date[i].ls].val;
    }

    res += date[date[ev].ls].val - date[date[sv].ls].val;
    if(k <= res) {
        for(auto it = ri.begin(); it != ri.end(); it++) {
            *it = date[*it].ls;
        }
        for(auto it = li.begin(); it != li.end(); it++) {
            *it = date[*it].ls;
        }
        return query(date[sv].ls, date[ev].ls, l, (l + r) / 2, k);
    }
    else {
        for(auto it = ri.begin(); it != ri.end(); it++) {
            *it = date[*it].rs;
        }
        for(auto it = li.begin(); it != li.end(); it++) {
            *it = date[*it].rs;
        }
        return query(date[sv].rs, date[ev].rs, (l + r) / 2, r, k - res);
    }
}

int main() {
    int t;
    int k, ans, temp;
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &m);
        sz = 0;
        for(int i = 0; i < n; i++) {
            scanf("%d", sourceDate + i);
            sortDate[sz++] = sourceDate[i];
        }
        for(int i = 0; i < m; i++) {
            scanf("%s", order);
            op[i].order = order[0];
            if(order[0] == 'Q') {
                scanf("%d%d%d", &op[i].l, &op[i].r, &op[i].k);
            }
            else {
                scanf("%d%d", &op[i].l, &op[i].r);
                sortDate[sz++] = op[i].r;
            }
        }
        sort(sortDate, sortDate + sz);
        sz = unique(sortDate, sortDate + sz) - sortDate;
        cntNode = 0;
        build(roots[0], 0, sz);
        for(int i = 0; i < n; i++) {
            k = lower_bound(sortDate, sortDate + sz, sourceDate[i]) - sortDate;
            update(roots[i], roots[i + 1], 0, sz, k, 1);
        }
        for(int i = 0; i <= n; i++) {
            //注意这里是 <= ,树状数组维护的是n + 1棵线段树
            bits[i] = roots[0];
        }
        for(int i = 0; i < m; i++) {
            if(op[i].order == 'Q') {
                li.clear();
                ri.clear();

                //下面两个循环计算的是在query函数中
                //使用树状数组思维求和时
                //需要访问的线段树根节点下标
                temp = op[i].r;
                while(temp) {
                    ri.push_back(bits[temp]);
                    temp -= temp & -temp;
                }

                temp = op[i].l - 1;
                while(temp) {
                    li.push_back(bits[temp]);
                    temp -= temp & -temp;
                }
                ans = query(roots[op[i].l - 1], roots[op[i].r], 0, sz, op[i].k);
                printf("%d\n", sortDate[ans]);
            }
            else {
                add(op[i].l, -1);
                sourceDate[op[i].l - 1] = op[i].r;
                add(op[i].l, 1);
            }
        }
    }
    return 0;
}

树状数组

一维树状数组

一维树状数组单点更新
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;

const int maxn = 500050;
LL bit[maxn];

void add(int k, int val) {
    while(k < maxn) {
        bit[k] += val;
        k += k & -k;
    }
}

LL sum(int k) {
    LL res = 0;
    while(k > 0) {
        res += bit[k];
        k -= k & -k;
    }
    return res;
}

int main() {
}
一维树状数组区间更新
//模板验证 HDU1556
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;

const int maxn = 100010;
int bit0[maxn];
int bit1[maxn];
int n;

void pointAdd(int *bit, int k, int val) {
    while(k <= n) {
        bit[k] += val;
        k += k & -k;
    }
}

int sum(int *bit, int k) {
    int res = 0;
    while(k > 0) {
        res += bit[k];
        k -= k & -k;
    }
    return res;
}

void rangeAdd(int l, int r, int val) {
    pointAdd(bit0, l, -val * (l - 1));
    pointAdd(bit1, l, val);
    pointAdd(bit0, r + 1, val * r);
    pointAdd(bit1, r + 1, -val);
}

int rangeSum(int l, int r) {
    return  sum(bit0, r) + r * sum(bit1, r) -
            sum(bit0, l - 1) - (l - 1) * sum(bit1, l - 1);
}

int main() {
    int l, r;
    while(scanf("%d", &n) && n) {
        memset(bit0, 0, sizeof(bit0));
        memset(bit1, 0, sizeof(bit1));
        for(int i = 0; i < n; i++) {
            scanf("%d%d", &l, &r);
            rangeAdd(l, r, 1);
        }
        for(int i = 1; i < n; i++) {
            printf("%d ", rangeSum(i, i));
        }
        printf("%d\n", rangeSum(n, n));
    }
    return 0;
}

二维树状数组

二维树状数组单点更新
const int maxn = 1000;
int bit[maxn][maxn];

void add(int x, int y, int val) {
    int memoryY = y;
    while(x <= maxn) {
        y = memoryY;
        while(y <= maxn) {
            bit[x][y] += val;
            y += y & -y;
        }
        x += x & -x;
    }
}

int sum(int x, int y) {
    int memoryY = y;
    int res = 0;
    while(x > 0) {
        y = memoryY;
        while(y > 0) {
            res += bit[x][y];
            y -= y & -y;
        }
        x -= x & -x;
    }
    return res;
}

字符串

KMP

#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
using namespace std;

const int maxn = 400001;
char source[maxn];
char pattern[maxn];
int next[maxn];
/*
int violentMatch(char *s, char *p) {
    int slen = strlen(s);
    int plen = strlen(p);
    int si = 0;
    int pi = 0;
    while(si < slen && pi < plen) {
        if(s[si] == p[pi]) {
            si++;
            pi++;
        }
        else {
            si = si - pi + 1;
            pi = 0;
        }
    }
    if(pi == plen) {
        return si - pi;
    }
    else {
        return -1;
    }
}
*/
void setNext(char *p, int *nx) {
    int plen = strlen(p);
    nx[0] = -1;
    int k = -1;
    int j = 0;
    while(j < plen - 1) {
        if(k == -1 || p[j] == p[k]) {
            k++;
            j++;
            if(p[j] != p[k]) {
                next[j] = k;
            }
            else {
                next[j] = next[k];
            }
        }
        else {
            k = next[k];
        }
    }
}
//判断p是否在s中,使用的next数组是p的next数组
//返回p在s中第一次出现的位置(0开始) 若s中没有p,则返回-1
int KMP(char *s, char *p) {
    int slen = strlen(s);
    int plen = strlen(p);
    int si = 0;
    int pi = 0;
    while(si <= slen - (plen - pi) && pi < plen) {
        if(pi == -1 || s[si] == p[pi]) {
            si++;
            pi++;
        }
        else {
            pi = next[pi];
        }
    }
    if(pi == plen) {
        return si - pi;
    }
    else {
        return -1;
    }
}

int main() {
    return 0;
}

扩展KMP

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace sdt;

const int maxn = 1000;
char source[maxn];
char pattern[maxn];
int next[maxn];
int extend[maxn];

//求解setNext使用 abcabc 作为子串,自己手动使用该算法算一遍应该就懂了...
void setNext(char *str, int *nx) {
    int len = strlen(str);
    nx[0] = len;
    for(nx[1] = 0; nx[1] < n - 1 && str[nx[1]] == str[nx[1] + 1]; nx[1]++) {
        continue;
    }
    int p0 = 1, p = nx[1] + 1 - 1;
    for(int i = 2; i < len; i++) {
        if(nx[i - p0] + i - 1 < p) {
            nx[i] = nx[i - p0];
        }
        else {
            nx[i] = max(0, p - i + 1);
            while(i + nx[i] < len && str[nx[i]] == str[i + nx[i]]) {
                nx[i]++;
            }
            p0 = i;
            p = i + nx[i] - 1;
        }
    }
}

void exKMP(char *sc, char *pat) {
    setNext(pat, next);
    int sLen = strlen(sc);
    int pLen = strlen(pat);
    for(extend[0] = 0; extend[0] < sLen && extend[0] < pLen && sc[extend[0]] == pat[extend[0]]; extend[0]++) {
        continue;
    }
    int p0 = 0;
    int p = extend[0] - 1;
    for(int i = 1; i < sLen; i++) {
        if(next[i - p0] + i - 1 < p) {
            extend[i] = next[i - p0];
        }
        else {
            int j = p - i + 1;
            if(j < 0) {
                j = 0;
            }
            while(j < pLen && i + j < sLen && sc[i + j] == pat[j]) {
                j++;
            }
            p0 = i;
            p = i + j - 1;
        }
    }
}

Manacher

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

const int maxn = 1000;
char source[maxn];          //原始字符串数据
char tempStr[maxn << 1];    //转换后的字符串
int maxLen[maxn << 1];      //maxLen[i]为以tempStr[i]为中心的最长回文子串最右边的位置到tempStr[i]的距离

int manacher() {
    int len = strlen(source);
    for(int i = 0; i < len; i++) {
        tempStr[2 * i] = '#';
        tempStr[2 * i + 1] = source[i];
    }
    tempStr[2 * len] = '#';
    tempStr[2 * len + 1] = '\0';
    len = 2 * len + 1;
    memset(maxLen, 0, sizeof(maxLen));
    maxLen[0] = 1;
    int p0 = 0, p = 0, ans = 0;     //p为当前匹配到了的最远的字符的下标,p0为取得该最远位置的中心的下标
    for(int i = 1; i < len; i++) {
        if(i < p) {
            if(i  + maxLen[2 * p0 - i] - 1 < p) {
                maxLen[i] = maxLen[2 * p0 - i];
            }
            else {
                maxLen[i] = p - i + 1;
                while(i - maxLen[i] >= 0 && i + maxLen[i] < len && tempStr[i - maxLen[i]] == tempStr[i + maxLen[i]]) {
                    maxLen[i]++;
                }
                if(i + maxLen[i] > p0 + maxLen[p0]) {
                    p0 = i;
                    p = i + maxLen[i] - 1;
                }
            }
        }
        else {
            maxLen[i] = 1;
            while(i - maxLen[i] >= 0 && i + maxLen[i] < len && tempStr[i - maxLen[i]] == tempStr[i + maxLen[i]]) {
                maxLen[i]++;
            }
            if(i + maxLen[i] > p0 + maxLen[p0]) {
                p0 = i;
                p = i + maxLen[i] - 1;
            }
        }
        ans = max(ans, maxLen[i] - 1);
    }
    return ans;
}

字典树

const alphNum = 26;
const int maxn = 1000;
int trie[maxn][alphNum];

void insert(const char *s) {
    int len = strlen(s);
    root = 0;
    int id;
    for(int i = 0; i < len; i++) {
        id = s[i] - 'a';
        if(tire[root][id] == 0) {
            trie[root][id] = ++tot;
        }
        root = trie[root][id];
    }
}

bool find(const char *s) {
    int len = strlen(s);
    int root = 0;
    int id;
    for(int i = 0; i < len; i++) {
        id = s[i] - 'a';
        if(trie[root][id] == 0) {
            return false;
        }
        root = trie[root][id];
    }
    return true;
}

AC自动机

//板子验证 HDU2222
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
class Node {
public:
    Node *next[26];
    Node *fail;
    int cnt;    //本来觉得用bool就好了,但是看了HDU2222之后,发现还是用int好一点
    Node() {
        for(int i = 0; i < 26; i++) {
            next[i] = NULL;
        }
        fail = NULL;
        cnt = 0;
    }
};

class ACauto {
public:
    Node *root;

    ACauto() {
        root = new Node;
    }

    ~ACauto() {
        clear();
        delete root;
    }

    void clear() {
        Node* temp;
        queue<Node *> q;
        q.push(root);
        while(!q.empty()) {
            temp = q.front();
            q.pop();
            for(int i = 0; i < 26; i++) {
                if(temp->next[i]) {
                    q.push(temp->next[i]);
                }
            }
            if(temp != root) {
                delete temp;
            }
        }
        for(int i = 0; i < 26; i++) {
            root->next[i] = NULL;
        }
    }

    void insert(const char * s) {
        Node *p = root;
        int id;
        for(int i = 0; s[i]; i++) {
            id = s[i] - 'a';
            if(p->next[id] == NULL) {
                p->next[id] = new Node;
            }
            p = p->next[id];
        }
        p->cnt++;
    }

    void buildFail() {
        Node *father;
        Node *fatherFail;
        queue<Node *> q;
        q.push(root);
        while(!q.empty()) {
            father = q.front();
            q.pop();
            for(int i = 0; i < 26; i++) {
                if(father->next[i]) {
                    fatherFail = father->fail;
                    while(fatherFail) {
                        if(fatherFail->next[i]) {
                            father->next[i]->fail = fatherFail->next[i];
                            break;
                        }
                        fatherFail = fatherFail->fail;
                    }
                    if(fatherFail == NULL) {
                        father->next[i]->fail = root;
                    }
                    q.push(father->next[i]);
                }
            }
        }
    }

    int find(const char *s) {
        int res = 0;
        Node *p = root;
        Node *temp;
        int id;
        for(int i = 0; s[i]; i++) {
            id = s[i] - 'a';
            while(p->next[id] == NULL && p != root) {
                p = p->fail;
            }
            p = p->next[id];
            if(p == NULL) {
                p = root;
            }
            temp = p;
            while(temp != root && temp->cnt != -1) {
                res += temp->cnt;
                temp->cnt = -1;
                temp = temp->fail;
            }
        }
        return res;
    }
};

const int maxKeyLen = 55;
const int maxTextLen = 1000010;
char key[maxKeyLen];
char str[maxTextLen];
ACauto ac;

int main() {
    int t;
    int n;
    scanf("%d", &t);
    while(t--) {
        ac.clear();
        scanf("%d", &n);
        for(int i = 0; i < n; i++) {
            scanf("%s", key);
            ac.insert(key);
        }
        ac.buildFail();
        scanf("%s", str);
        printf("%d\n", ac.find(str));
    }
    return 0;
}

AC自动机+矩阵快速幂

/*
模板测试 POJ2778
在建立失效指针的同时,统计安全节点的数量
以此减小矩阵的规模
测试数据
比如当输入的字符串为
ATCG
T
的时候,安全节点只有两个,为root和A
此时cntNode = 2
*/
#include<iostream>
#include<algorithm>
#include<queue>
#include<set>
#include<vector>
#include<map>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;

class Node {
public:
    Node *next[4];
    Node *fail;
    bool isTail;
    int tag;
    Node() {
        memset(next, 0, sizeof(next));
        fail = NULL;
        isTail = false;
        tag = -1;
    }
};

class ACauto {
private:
    Node *root;
    map<char, int> trans;

public:
    int cntNode;    //统计安全节点的数量
    ACauto() {
        root = new Node();
        root->tag = 0;
        cntNode = 1;
        trans['A'] = 0;
        trans['C'] = 1;
        trans['T'] = 2;
        trans['G'] = 3;
    }

    ~ACauto() {
        clear();
        delete root;
    }

    void clear() {
        queue<Node *> que;
        Node *temp;
        que.push(root);
        while(!que.empty()) {
            temp = que.front();
            que.pop();
            for(int i = 0; i < 4; i++) {
                if(temp->next[i] != NULL) {
                    que.push(temp->next[i]);
                }
            }
            if(temp != root) {
                delete temp;
            }
        }
        for(int i = 0; i < 4; i++) {
            root->next[i] = NULL;
        }
        cntNode = 1;
    }

    void insert(const char *s) {
        Node *p = root;
        int id;
        for(int i = 0; s[i]; i++) {
            id = trans[s[i]];
            if(p->next[id] == NULL) {
                if(p->isTail) {
                    return ;
                }
                p->next[id] = new Node();
            }
            p = p->next[id];
        }
        p->isTail = true;
    }

    void buildFail() {
        Node *father;
        Node *fatherFail;
        queue<Node *> que;
        que.push(root);
        while(!que.empty()) {
            father = que.front();
            que.pop();
            for(int i = 0; i < 4; i++) {
                if(father->next[i] != NULL && !(father->next[i]->isTail)) {
                    fatherFail = father->fail;
                    while(fatherFail != NULL) {
                        if(fatherFail->next[i]) {
                            father->next[i]->fail = fatherFail->next[i];
                            break;
                        }
                        fatherFail = fatherFail->fail;
                    }
                    if(fatherFail == NULL) {
                        father->next[i]->fail = root;
                    }
                    if(father->next[i]->fail->isTail) {
                        father->next[i]->isTail = true;
                    }
                    else {
                        father->next[i]->tag = cntNode++;
                        que.push(father->next[i]);
                    }
                }
            }
        }
    }

    void buildMatrix(LL mat[][110]) {
        for(int i = 0; i < cntNode; i++) {
            for(int j = 0; j < cntNode; j++) {
                mat[i][j] = 0;
            }
        }
        queue<Node *> que;
        Node *father, *temp;
        que.push(root);
        while(!que.empty()) {
            father = que.front();
            que.pop();
            for(int i = 0; i < 4; i++) {
                if(father->next[i] != NULL && !(father->next[i]->isTail)) {
                    que.push(father->next[i]);
                }
                temp = father;
                while(temp != NULL && temp->next[i] == NULL) {
                   temp = temp->fail;
                }
                if(temp == NULL) {
                    mat[father->tag][0]++;
                }
                else if(temp->next[i]->tag >= 0){
                    mat[father->tag][temp->next[i]->tag]++;
                }
            }
        }
    }
};

const LL MOD = 100000;
char str[20];
LL matrix1[110][110];
LL matrix2[110][110];
LL matrix3[110][110];
ACauto ac;

void mul(LL matrix1[110][110], LL matrix2[110][110], int len) {
    for(int i = 0; i < len; i++) {
        for(int j = 0; j < len; j++) {
            matrix3[i][j] = 0;
        }
    }
    for(int i = 0; i < len; i++) {
        for(int j = 0; j <len; j++) {
            for(int k = 0; k < len; k++) {
                matrix3[i][j] = ((matrix1[i][k] * matrix2[k][j] % MOD) + matrix3[i][j]) % MOD;
            }
        }
    }
    for(int i = 0; i < len; i++) {
        for(int j = 0; j < len; j++) {
            matrix1[i][j] = matrix3[i][j];
        }
    }
}

void power(LL matrix1[][110], int n, int len) {
    for(int i = 0; i < len; i++) {
        for(int j = 0; j < len; j++) {
            if(i != j) {
                matrix2[i][j] = 0;
            }
            else {
                matrix2[i][j] = 1;
            }
        }
    }
    while(n) {
        if(n & 1) {
            mul(matrix2, matrix1, len);
        }
        mul(matrix1, matrix1, len);
        n >>= 1;
    }
    for(int i = 0; i < len; i++) {
        for(int j = 0; j < len; j++) {
            matrix1[i][j] = matrix2[i][j];
        }
    }
}


int main() {
    int m, n;
    LL ans;
    while(scanf("%d%d", &m, &n) == 2) {
        ac.clear();
        for(int i = 0; i < m; i++) {
            scanf("%s", str);
            ac.insert(str);
        }
        ac.buildFail();
        ac.buildMatrix(matrix1);
        power(matrix1, n, ac.cntNode);
        ans = 0;
        for(int j = 0; j < ac.cntNode; j++) {
            ans = (ans + matrix1[0][j]) % MOD;
        }
        printf("%lld\n", ans);
    }
    return 0;
}

后缀数组

//时间复杂度大约为(4~6)O(n * floor(log n))
#include<iostream>
#include<cstdio>
#include<cstring>
//#define DEBUG
using namespace std;

const int maxn = 1000;
char str[maxn];             //需要求后缀数组的字符串
int suffixArray[maxn];      //suffixArray[i]为排名第i的子串的起点
                            //在计算过程中,如果出现子串相等的情况,则起点靠前的子串排名靠前
int cnt[maxn];              //统计排名(_rank)为i的二元组的数量
int tempArray1[maxn];
int tempArray2[maxn];
int n;
int Rank[maxn];             //Rank[i]为后缀i的排名
int height[maxn];           //height[i]为后缀i和后缀i-1的最长公共前缀长度

#ifdef DEBUG
void show(int *a) {
    for(int i = 0; i < n; i++) {
        cout << a[i] << " ";
    }
    cout << endl;
}
#endif // DEBUG

void buildSa(int m) {
    int countKind;                      //统计二元组的种类
    int *_rank = tempArray1;            //_rank[i]为,以str[i]开头的长度为k的子串的排名
                                        //即刘汝佳《算法竞赛入门经典——训练指南》P220中每幅图最下面的一行
                                        //若子串相等,则排名相同

/* *************************************************************************************************** */
    memset(cnt, 0, sizeof(int) * m);
    for(int i = 0; i < n; i++) {        //cnt先暂时用来统计每种字符出现的数量
        cnt[(int)str[i]]++;
    }
    for(int i = 1; i < m; i++) {
        cnt[i] += cnt[i - 1];
    }
    for(int i = n - 1; i >= 0; i--) {   //计算名次为--cnt[str[i]]的子串的起点
        //1 获取字符串中第i个位置的字符
        //2 通过--cnt[str[i]]计算出以str[i]开头的长度为1的子串的排名
        //3 则排名为--cnt[str[i]]的子串的起点下标为i
        suffixArray[--cnt[(int)str[i]]] = i;
    }
    countKind = 0;
    for(int i = 0, preKey = -1; i < n; i++) {
        if(str[suffixArray[i]] == preKey) {
            _rank[suffixArray[i]] = countKind;
        }
        else {
            preKey = str[suffixArray[i]];
            _rank[suffixArray[i]] = ++countKind;
        }
    }
#ifdef DEBUG
    show(_rank);
    show(suffixArray);
#endif // DEBUG
    if(countKind == n) {
        return ;
    }
//到目前为止,都是为了计算出字串长度为1时的排名
//_rank在接下来会用于计算第一和第二关键字
//当前计算出的_rank就是下一次迭代中二元组的第一关键字
/* *************************************************************************************************** */
    for(int k = 1; k <= n; k <<= 1) {
        //每个子串的长度为2 * k
        int *firstKey = _rank;                                                  //上一重迭代中计算出的_rank就是当前迭代中二元组的第一关键字
        int *secondRankIndex = _rank == tempArray1 ? tempArray2 : tempArray1;   //secondRankIndex第二关键字排名第i的二元组的下标
        for(int i = 0; i < k; i++) {
            secondRankIndex[i] = n - k + i;
        }
        for(int i = 0, j = k; i < n; i++) {
            if(suffixArray[i] >= k) {
                secondRankIndex[j++] = suffixArray[i] - k;
            }
        }
        memset(cnt, 0, sizeof(int) * (countKind + 1));
        for(int i = 0; i < n; i++) {
            cnt[firstKey[i]]++;
        }
        for(int i = 1; i <= countKind; i++) {
            cnt[i] += cnt[i - 1];
        }
        for(int i = n - 1; i >= 0; i--) {
            //1 找到第二关键字排名第i的二元组的下标secondRankIndex[i],令index = secondRankIndex[i]
            //2 找到该二元组的第一关键字firstKey[index],令key = firstKey[index]
            //3 求出该二元组的排名--cnt[key]
            suffixArray[--cnt[firstKey[secondRankIndex[i]]]] = secondRankIndex[i];
        }
        //程序执行到这里的时候,_rank中保存的是上一重迭代的计算结果
        //_rank[i]即为firstKey[i]即为第i个二元组的第一关键字
        //当i>=k时,firstKey[i] = secondKey[i - k]即第i-k个二元组的第二关键字
        _rank = secondRankIndex;    //现在secondRankIndex用不上了,就把它的空间拿给_rank用
                                    //但是firstKey保持不变,依然是指向上一重迭代中计算出来的_rank
        int preFirstKey = -1, preSecondKey = -1;
        int curFirstKey, curSecondKey;
        countKind = 0;
        for(int i = 0; i < n; i++) {
            curFirstKey = firstKey[suffixArray[i]];
            curSecondKey = suffixArray[i] >= n - k ? 0 : firstKey[suffixArray[i] + k];
            if(curFirstKey == preFirstKey && curSecondKey == preSecondKey) {
                _rank[suffixArray[i]] = countKind;
            }
            else {
                preFirstKey = curFirstKey;
                preSecondKey = curSecondKey;
                _rank[suffixArray[i]] = ++countKind;
            }
        }
#ifdef DEBUG
        show(_rank);
        show(suffixArray);
#endif // DEBUG
        if(countKind >= n) {
            return ;
        }
    }
}

void getHeight() {
    /*
        通过h[i] >= h[i - 1] - 1线性计算height
        设suffix(k)是排在suffix(i - 1)前一名的后缀
        即Rank[k] = Rank[i - 1] - 1
        则它们的最长公共前缀的长度为h[i - 1]即height[Rank[i - 1]]
        那么suffix(k + 1)排在suffix(i)前面
        (当h[i - 1] <= 1时,原式显然成立,故下面均假设h[i - 1] > 1)
        并且suffix(k + 1)和suffix(i)的最长公共前缀为h[i - 1] - 1
        于是,suffix(i)与它前面一名的最长公共前缀至少为h[i - 1] - 1
    */
    for(int i = 0; i < n; i++) {
        Rank[suffixArray[i]] = i;
    }
    for(int i = 0, j, k = 0; i < n; i++) {
        if(k > 0) {
            k--;
        }
        if(Rank[i] == 0) {
            height[Rank[i]] = 0;
            k = 0;
            continue;
        }
        j = suffixArray[Rank[i] - 1];
        while(i + k < n && j + k < n && str[i + k] == str[j + k]) {
            k++;
        }
        height[Rank[i]] = k;
    }
}

int main() {
    while(cin >> str) {
        n = strlen(str);
        buildSa('z' + 1);
    }
    return 0;
}

回文树(回文自动机)

指针版(性能较数组版低)

//HDU3948
//249ms	22.9MB	2544B	
//计算不同回文子串的数量
//注意使用时字符串下标从1开始
//输入时需要scanf("%s", str + 1);
#include<iostream>
#include<string>
#include<queue>
#include<cstdio>
#include<cstring>
using namespace std;

class Node {
public:
    enum{SIZE = 26};
    int len;
    int failLen;
    Node* next[SIZE];
    Node* fail;

    Node(int _len = 0, int _failLen = -1, Node *f = NULL) {
        len = _len;
        failLen = _failLen;
        for(int i = 0; i < SIZE; i++) {
            next[i] = NULL;
        }
        fail = len == -1 ? this : f;
    }
};

class PalindromeTree{
public:
    Node* odd;
    Node* even;
    int countNode;

    PalindromeTree() {
        odd = new Node(-1);
        even = new Node(0, -1, odd);
        countNode = 0;
    }

    void build(const char *s) {
        clear();
        Node *p = odd;
        for(int i = 1; s[i]; i++) {
            while(s[i - p->len - 1] != s[i]) {
                p = p->fail;
            }
            int id = s[i] - 'a';
            if(p->next[id] != NULL) {
                p = p->next[id];
            }
            else {
                countNode++;
                Node *temp = p;
                p->next[id] = new Node(p->len + 2, p->failLen + 1);
                p = p->next[id];
                if(temp->len == -1) {
                    p->fail = even;
                }
                else {
                    temp = temp->fail;
                    while(s[i - temp->len - 1] != s[i]) {
                        temp = temp->fail;
                    }
                    p->fail = temp->next[id];
                }
            }
        }
    }

    void clear() {
        queue<Node *> que;
        que.push(odd);
        que.push(even);
        Node *temp;
        while(!que.empty()) {
            temp = que.front();
            que.pop();
            for(int i = 0; i < Node::SIZE; i++) {
                if(temp->next[i] != NULL) {
                    que.push(temp->next[i]);
                }
            }
            if(temp == odd || temp == even) {
                continue;
            }
            delete temp;
        }
        for(int i = 0; i < Node::SIZE; i++) {
            odd->next[i] = NULL;
            even->next[i] = NULL;
        }
        countNode = 0;
    }

    ~PalindromeTree() {
        clear();
        delete odd;
        delete even;
    }
};

const int maxn = 110000;
char str[maxn];
PalindromeTree pt;

int main() {
    int t;
    int cs = 0;
    scanf("%d", &t);
    while(t--) {
        scanf("%s", str + 1);
        pt.build(str);
        printf("Case #%d: %d\n", ++cs, pt.countNode);
        pt.clear();
    }
    return 0;
}

数组版

//板子验证同上
//78ms	11.3MB	1586B	
#include<iostream>
#include<string>
#include<queue>
#include<map>
#include<cstdio>
#include<cstring>
using namespace std;

struct Node {
    enum{SIZE = 26};
    int len;
    int next[Node::SIZE];
    int fail;

    void init(int _len = 0, int _fail = -1) {
        len = _len;
        for(int i = 0; i < SIZE; i++) {
            next[i] = -1;
        }
        fail = len == -1 ? 0 : _fail;
    }
};

const int maxn = 100010;
Node tree[maxn];
char str[maxn];
int countNode;

void clear() {
    tree[0].init(-1, 0);
    tree[1].init(0, 0);
    countNode = 2;
}

void build(const char *s) {
    clear();
    int p = 0;
    for(int i = 1; s[i]; i++) {
        while(s[i - tree[p].len - 1] != s[i]) {
            p = tree[p].fail;
        }
        int id = s[i] - 'a';
        if(tree[p].next[id] != -1) {
            p = tree[p].next[id];
        }
        else {
            int temp = p;
            tree[p].next[id] = countNode;
            tree[countNode].init(tree[p].len + 2);
            p = tree[p].next[id];
            if(temp == 0) {
                tree[p].fail = 1;
            }
            else {
                temp = tree[temp].fail;
                while(s[i - tree[temp].len - 1] != s[i]) {
                    temp = tree[temp].fail;
                }
                tree[p].fail = tree[temp].next[id];
            }
            countNode++;
        }
    }
}

int main() {
    int t;
    int cs = 0;
    scanf("%d", &t);
    while(t--) {
        scanf("%s", str + 1);
        build(str);
        printf("Case #%d: %d\n",++cs, countNode - 2);
    }
    return 0;
}

后缀自动机

1、两个串的最长公共子串
2、统计出现次数最多的长度为n的串一共出现了多少次,使用基数排序
3、
4、

两个串的最长公共子串

//两个串的最长公共子串
//https://www.spoj.com/problems/LCS/en/
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;

const int SIZE = 26;
const int START = int('a');
const int maxn = 260000;
char str1[maxn];
char str2[maxn];
struct State {
    int len;
    int next[SIZE];
    int link;
}st[maxn * 2];
int sz;
int last;

void initSa() {
    sz = 1;
    last = 0;
    st[0].len = 0;
    st[0].link = -1;
    for(int i = 0; i < SIZE; i++) {
        st[0].next[i] = -1;
    }
}

void addChar(char c) {
    int cur = sz++;
    st[cur].len = st[last].len + 1;
    for(int i = 0; i < SIZE; i++) {
        st[cur].next[i] = -1;
    }
    int id = c - START;
    int p = last;
    for(; p != -1 && st[p].next[id] == -1; p = st[p].link) {
        st[p].next[id] = cur;
    }
    if(p == -1) {
        st[cur].link = 0;
    }
    else {
        int q = st[p].next[id];
        if(st[p].len + 1 == st[q].len) {
            st[cur].link = q;
        }
        else {
            int clone = sz++;
            st[clone].len = st[p].len + 1;
            st[clone].link = st[q].link;
            for(int i = 0; i < SIZE; i++) {
                st[clone].next[i] = st[q].next[i];
            }
            for(; p != -1 && st[p].next[id] == q; p = st[p].link) {
                st[p].next[id] = clone;
            }
            st[q].link = st[cur].link = clone;
        }
    }
    last = cur;
}

int lcs() {
    initSa();
    for(int i = 0; str1[i]; i++) {
        addChar(str1[i]);
    }
    int p = 0;
    int ans = 0;
    int curLen = 0;
    int id;
    for(int i = 0; str2[i]; i++) {
        id = str2[i] - START;
        while(p && st[p].next[id] == -1) {
            p = st[p].link;
            curLen = st[p].len;
        }
        if(st[p].next[id] != -1) {
            p = st[p].next[id];
            curLen++;
        }
        ans = max(ans, curLen);
    }
    return ans;
}

int main() {
    while(scanf("%s%s", str1, str2) == 2) {
        printf("%d\n", lcs());
    }
    return 0;
}

多个串的最长公共子串

//LCS2 - Longest Common Substring II 
//https://www.spoj.com/problems/LCS2/en/
#include<iostream>
#include<algorithm>
#include<set>
#include<cstdio>
#include<cstring>
using namespace std;

const int maxn = 110000;
const int SIZE = 26;
struct State {
    int len;
    int matcheLen;
    int link;
    int next[SIZE];
}st[maxn * 2];
int sz;
int last;
char str[maxn];
int bucket[maxn];
int Rank[maxn * 2];

void initSa() {
    sz = 1;
    last = 0;
    st[0].len = 0;
    st[0].link = -1;
    st[0].matcheLen = 0;
    for(int i = 0; i < SIZE; i++) {
        st[0].next[i] = -1;
    }
}

void addChar(int c) {
    int cur = sz++;
    st[cur].len = st[last].len + 1;
    st[cur].matcheLen = 0;
    for(int i = 0; i < SIZE; i++) {
        st[cur].next[i] = -1;
    }
    int p = last;
    for(; p != -1 && st[p].next[c] == -1; p = st[p].link) {
        st[p].next[c] = cur;
    }
    if(p == -1) {
        st[cur].link = 0;
    }
    else {
        int q = st[p].next[c];
        if(st[p].len + 1 == st[q].len) {
            st[cur].link = q;
        }
        else {
            int clone = sz++;
            st[clone].len = st[p].len + 1;
            st[clone].link = st[q].link;
            st[clone].matcheLen = 0;
            for(int i = 0; i < SIZE; i++) {
                st[clone].next[i] = st[q].next[i];
            }
            st[q].link = st[cur].link = clone;
            for(; p != -1 && st[p].next[c] == q; p = st[p].link) {
                st[p].next[c] = clone;
            }
        }
    }
    last = cur;
}

void match(const char* s) {
    int cur = 0;
    int curLen = 0;
    int id;
    for(int i = 0; s[i]; i++) {
        id = s[i] - 'a';
        while(cur && st[cur].next[id] == -1) {
            cur = st[cur].link;
            st[cur].matcheLen = curLen = st[cur].len;
        }
        if(st[cur].next[id] != -1) {
            curLen++;
            cur = st[cur].next[id];
            st[cur].matcheLen = max(st[cur].matcheLen, curLen);
        }
    }
    for(int i = 0; i < sz; i++) {
        if(st[Rank[i]].matcheLen) {
            st[st[Rank[i]].link].matcheLen = st[st[Rank[i]].link].len;
        }
        st[Rank[i]].len = min(st[Rank[i]].len, st[Rank[i]].matcheLen);
        st[Rank[i]].matcheLen = 0;
    }
}

void radixSort(int len) {
    for(int i = 0; i < sz; i++) {
        ++bucket[st[i].len];
    }
    for(int i = len - 1; i >= 0; i--) {
        bucket[i] += bucket[i + 1];
    }
    for(int i = 0; i < sz; i++) {
        Rank[--bucket[st[i].len]] = i;
    }
}

int main() {
    //freopen("wa.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    initSa();
    scanf("%s", str);
    int len;
    for(len = 0; str[len]; len++) {
        addChar(str[len] - 'a');
    }
    radixSort(len);
    while(scanf("%s", str) == 1) {
        match(str);
    }
    int ans = 0;
    for(int i = 0; i < sz; i++) {
        ans = max(ans, st[i].len);
    }
    printf("%d\n", ans);
    return 0;
}

出现次数查询

//Substrings SPOJ - NSUBSTR 
//https://www.spoj.com/problems/NSUBSTR/en/
//使用基数排序
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;

const int maxn = 260000;
const int SIZE = 26;
struct {
    int len;
    int next[SIZE];
    int link;
    int cnt;
}st[maxn * 2];
int bucket[maxn];
int Rank[maxn * 2];
int ans[maxn];
char str[maxn];
int sz;
int last;

void initSa() {
    last = 0;
    sz = 1;
    st[0].cnt = 0;
    st[0].len = 0;
    st[0].link = -1;
    for(int i = 0; i < SIZE; i++) {
        st[0].next[i] = -1;
    }
}

void addChar(int c) {
    int cur = sz++;
    st[cur].len = st[last].len + 1;
    st[cur].cnt = 1;
    for(int i = 0; i < SIZE; i++) {
        st[cur].next[i] = -1;
    }
    int p = last;
    for(; p != -1 && st[p].next[c] == -1; p = st[p].link) {
        st[p].next[c] = cur;
    }
    if(p == -1) {
        st[cur].link = 0;
    }
    else {
        int q = st[p].next[c];
        if(st[p].len + 1 == st[q].len) {
            st[cur].link = q;
        }
        else {
            int clone = sz++;
            st[clone].len = st[p].len + 1;
            st[clone].cnt = 0;
            st[clone].link = st[q].link;
            for(int i = 0; i < SIZE; i++) {
                st[clone].next[i] = st[q].next[i];
            }
            st[q].link = st[cur].link = clone;
            for(; p != -1 && st[p].next[c] == q; p = st[p].link) {
                st[p].next[c] = clone;
            }
        }
    }
    last = cur;
}

void radixSort(int len) {
    memset(bucket, 0, sizeof(bucket));
    for(int i = 0; i < sz; i++) {
        bucket[st[i].len]++;
    }
    for(int i = len - 1; i >= 0; i--) {
        bucket[i] += bucket[i + 1];
    }
    for(int i = 0; i < sz; i++) {
        Rank[--bucket[st[i].len]] = i;
    }
}

void solve() {
    initSa();
    int len;
    for(len = 0; str[len]; len++) {
        addChar(str[len] - 'a');
    }
    radixSort(len);
    for(int i = 0; i < sz; i++) {
        st[st[Rank[i]].link].cnt += st[Rank[i]].cnt;
    }
    memset(ans, 0, sizeof(ans));
    for(int i = 0; i < sz; i++) {
        ans[st[i].len] = max(ans[st[i].len], st[i].cnt);
    }
    for(int i = 1; i <= len; i++) {
        printf("%d\n", ans[i]);
    }
}

int main() {
    while(scanf("%s", str) == 1) {
        solve();
    }
    return 0;
}

1 图论 3 1.1 术语 3 1.2 独立集、覆盖集、支配集之间关系 3 1.3 DFS 4 1.3.1 割顶 6 1.3.2 桥 7 1.3.3 强连通分量 7 1.4 最小点基 7 1.5 拓扑排序 7 1.6 欧拉路 8 1.7 哈密顿路(正确?) 9 1.8 Bellman-ford 9 1.9 差分约束系统(用bellman-ford解) 10 1.10 dag最短路径 10 1.11 二分图匹配 11 1.11.1 匈牙利算法 11 1.11.2 KM算法 12 1.12 网络流 15 1.12.1 最大流 15 1.12.2 上下界的网络的最大流 17 1.12.3 上下界的网络的最小流 17 1.12.4 最小费用最大流 18 1.12.5 上下界的网络的最小费用最小流 21 2 数论 21 2.1 最大公约数gcd 21 2.2 最小公倍数lcm 22 2.3 快速幂取模B^LmodP(O(logb)) 22 2.4 Fermat小定理 22 2.5 Rabin-Miller伪素数测试 22 2.6 Pollard-rho 22 2.7 扩展欧几里德算法extended-gcd 24 2.8 欧拉定理 24 2.9 线性同余方程ax≡b(mod n) 24 2.10 中国剩余定理 25 2.11 Discrete Logging(BL == N (mod P)) 26 2.12 N!最后一个不为0的数字 27 2.13 2^14以内的素数 27 3 数据结构 31 3.1 堆(最小堆) 31 3.1.1 删除最小值元素: 31 3.1.2 插入元素和向上调整: 32 3.1.3 堆的建立 32 3.2 并查集 32 3.3 树状数组 33 3.3.1 LOWBIT 33 3.3.2 修改a[p] 33 3.3.3 前缀和A[1]+…+A[p] 34 3.3.4 一个二维树状数组的程序 34 3.4 线段树 35 3.5 字符串 38 3.5.1 字符串哈希 38 3.5.2 KMP算法 40 4 计算几何 41 4.1 直线交点 41 4.2 判断线段相交 41 4.3 三点外接圆圆心 42 4.4 判断点在多边形内 43 4.5 两圆交面积 43 4.6 最小包围圆 44 4.7 经纬度坐标 46 4.8 凸包 46 5 Problem 48 5.1 RMQ-LCA 48 5.1.1 Range Minimum Query(RMQ) 49 5.1.2 Lowest Common Ancestor (LCA) 53 5.1.3 Reduction from LCA to RMQ 56 5.1.4 From RMQ to LCA 57 5.1.5 An<O(N), O(1)> algorithm for the restricted RMQ 60 5.1.6 An AC programme 61 5.2 最长公共子序列LCS 64 5.3 最长上升子序列/最长不下降子序列(LIS) 65 5.3.1 O(n^2) 65 5.3.2 O(nlogn) 66 5.4 Joseph问题 67 5.5 0/1背包问题 68 6 组合数学相关 69 6.1 The Number of the Same BST 69 6.2 排列生成 71 6.3 逆序 72 6.3.1 归并排序求逆序 72 7 数值分析 72 7.1 二分法 72 7.2 迭代法(x=f(x)) 73 7.3 牛顿迭代 74 7.4 数值积分 74 7.5 高斯消元 75 8 其它 77
1 图论 3 1.1 术语 3 1.2 独立集、覆盖集、支配集之间关系 3 1.3 DFS 4 1.3.1 割顶 6 1.3.2 桥 7 1.3.3 强连通分量 7 1.4 最小点基 7 1.5 拓扑排序 7 1.6 欧拉路 8 1.7 哈密顿路(正确?) 9 1.8 Bellman-ford 9 1.9 差分约束系统(用bellman-ford解) 10 1.10 dag最短路径 10 1.11 二分图匹配 11 1.11.1 匈牙利算法 11 1.11.2 KM算法 12 1.12 网络流 15 1.12.1 最大流 15 1.12.2 上下界的网络的最大流 17 1.12.3 上下界的网络的最小流 17 1.12.4 最小费用最大流 18 1.12.5 上下界的网络的最小费用最小流 21 2 数论 21 2.1 最大公约数gcd 21 2.2 最小公倍数lcm 22 2.3 快速幂取模B^LmodP(O(logb)) 22 2.4 Fermat小定理 22 2.5 Rabin-Miller伪素数测试 22 2.6 Pollard-rho 22 2.7 扩展欧几里德算法extended-gcd 24 2.8 欧拉定理 24 2.9 线性同余方程ax≡b(mod n) 24 2.10 中国剩余定理 25 2.11 Discrete Logging(BL == N (mod P)) 26 2.12 N!最后一个不为0的数字 27 2.13 2^14以内的素数 27 3 数据结构 31 3.1 堆(最小堆) 31 3.1.1 删除最小值元素: 31 3.1.2 插入元素和向上调整: 32 3.1.3 堆的建立 32 3.2 并查集 32 3.3 树状数组 33 3.3.1 LOWBIT 33 3.3.2 修改a[p] 33 3.3.3 前缀和A[1]+…+A[p] 34 3.3.4 一个二维树状数组的程序 34 3.4 线段树 35 3.5 字符串 38 3.5.1 字符串哈希 38 3.5.2 KMP算法 40 4 计算几何 41 4.1 直线交点 41 4.2 判断线段相交 41 4.3 三点外接圆圆心 42 4.4 判断点在多边形内 43 4.5 两圆交面积 43 4.6 最小包围圆 44 4.7 经纬度坐标 46 4.8 凸包 46 5 Problem 48 5.1 RMQ-LCA 48 5.1.1 Range Minimum Query(RMQ) 49 5.1.2 Lowest Common Ancestor (LCA) 53 5.1.3 Reduction from LCA to RMQ 56 5.1.4 From RMQ to LCA 57 5.1.5 An algorithm for the restricted RMQ 60 5.1.6 An AC programme 61 5.2 最长公共子序列LCS 64 5.3 最长上升子序列/最长不下降子序列(LIS) 65 5.3.1 O(n^2) 65 5.3.2 O(nlogn) 66 5.4 Joseph问题 67 5.5 0/1背包问题 68 6 组合数学相关 69 6.1 The Number of the Same BST 69 6.2 排列生成 71 6.3 逆序 72 6.3.1 归并排序求逆序 72 7 数值分析 72 7.1 二分法 72 7.2 迭代法(x=f(x)) 73 7.3 牛顿迭代 74 7.4 数值积分 74 7.5 高斯消元 75 8 其它 77
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值