字符串数据结构模板整理

1.Trie(字典树、前缀树)

题目:http://hihocoder.com/problemset/problem/1014
题意:开始给定一些字符串,求每次以询问的字符串为前缀的字符串个数
思路:Trie
代码:

#include<bits/stdc++.h>
using namespace std;

const int N = 1000005;
const int C_size = 26;
struct Trie{
    int trieN;
    int ch[N][C_size], val[N];
    void init(){
        memset(ch[0],0,sizeof(ch[0]));
        trieN = -1;
        newnode();
    }
    int newnode(){
        memset(ch[++trieN], 0, sizeof(ch[0]));
        val[trieN] = 0;//fail[trieN] = 0;
        return trieN;
    }
    void insert(char *str){
        int cur = 0;
        for(int i = 0;str[i] != '\0';i++){
            int d = str[i] - 'a';
            if(!ch[cur][d])
                ch[cur][d] = newnode();
            cur = ch[cur][d];
            val[cur]++;
        }
    }
    int query(char *str){
        int cur = 0;
        for (int i = 0;str[i] != '\0';i++){
            int d = str[i] - 'a';
            if(ch[cur][d])
                cur = ch[cur][d];
            else//ÎÞ·¨Æ¥Åä
                return 0;
        }
        return val[cur];
    }
}trie;

int main(){
    int n,m;
    char str[15];
    while(~scanf("%d",&n)){
        trie.init();
        for(int i = 0;i < n;i++){
            scanf("%s",str);
            trie.insert(str);
        }
        scanf("%d",&m);
        while(m--){
            scanf("%s",str);
            printf("%d\n",trie.query(str));
        }
    }
    return 0;
}

题目:http://acm.split.hdu.edu.cn/showproblem.php?pid=5687
题意:对字符串的简单插入、删除、查询
代码:

#include<bits/stdc++.h>
using namespace std;

const int N = 3000005;
const int C_size = 26;
struct Trie{
    int trieN;
    int ch[N][C_size], val[N];
    void init(){
        memset(ch[0],0,sizeof(ch[0]));
        trieN = -1;
        newnode();
    }
    int newnode(){
        memset(ch[++trieN], 0, sizeof(ch[0]));
        val[trieN] = 0;//fail[trieN] = 0;
        return trieN;
    }
    void insert(char *str){
        int cur = 0;
        for(int i = 0;str[i] != '\0';i++){
            int d = str[i] - 'a';
            if(!ch[cur][d])
                ch[cur][d] = newnode();
            cur = ch[cur][d];
            val[cur]++;
        }
    }
    void del(char *str){
        int cur = 0;
        for(int i = 0;str[i] != '\0';i++){
            int d = str[i] - 'a';
            if(!ch[cur][d])
                return;
            cur = ch[cur][d];
        }
        int x = 0;
        for(int i = 0;str[i] != '\0';i++){
            int d = str[i] - 'a';
            if(!ch[x][d])
                return;
            int pre = x;
            x = ch[x][d];
            val[x] -= val[cur];
            if(val[x] == 0)
                ch[pre][d] = 0;
        }
    }
    int query(char *str){
        int cur = 0;
        for (int i = 0;str[i] != '\0';i++){
            int d = str[i] - 'a';
            if(ch[cur][d])
                cur = ch[cur][d];
            else//ÎÞ·¨Æ¥Åä
                return 0;
        }
        return val[cur];
    }
}trie;

int main(){
    int n;
    char str[35],type[10];
    scanf("%d",&n);
    trie.init();
    for(int i = 0;i < n;i++){
        scanf("%s %s",type,str);
        if(type[0] == 'i')
            trie.insert(str);
        else if(type[0] == 'd')
            trie.del(str);
        else{
            if(trie.query(str))
                puts("Yes");
            else
                puts("No");
        }
    }
    return 0;
}

题目:http://codeforces.com/contest/706/problem/D
题意:给你一个集合,对集合操作,“+”代表加入一个数,“-代表”删除一个数,“?”代表查询x与集合中任意数xor的最大值
思路:将每个数拆成二进制的形式插入Trie树中,按照字符串的操作来即可
代码:

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

const int N = 4000005;
int a[N][4],sum[N];
int sz;
void Init()
{
    memset(a,0,sizeof(a));
    memset(sum,0,sizeof(sum));
    sz = 1;
}

void Insert(int x)
{
    int num[35];
    memset(num,0,sizeof(num));
    int cnt = 0;
    while(x)
    {
        num[cnt++] = x % 2;
        x /= 2;
    }
    int u = 0,n = 34;
    for(int i = n;i >= 0;i--)
    {
        if(!a[u][num[i]])
            a[u][num[i]] = sz++;
        u = a[u][num[i]];
        sum[u]++;
    }
}

void Delete(int x)
{
    int num[35];
    memset(num,0,sizeof(num));
    int cnt = 0;
    while(x)
    {
        num[cnt++] = x % 2;
        x /= 2;
    }
    int u = 0,n = 34;
    for(int i = n;i >= 0;i--)
    {
        int v = a[u][num[i]];
        sum[v]--;
        if(!sum[v])
            a[u][num[i]] = 0;
        u = v;
    }
}

int Query(int y)
{
    int num[35];
    memset(num,0,sizeof(num));
    int cnt = 0;
    while(y)
    {
        num[cnt++] = y % 2;
        y /= 2;
    }
    for(int i = 0;i <= 34;i++)
        num[i]= num[i] ? 0 : 1;
    int u = 0,n = 34,v,w;
    int ans = 0;
    for(int i = n;i >= 0;i--)
    {
        if(num[i])
        {
            v = 1;
            w = 0;
        }
        else
        {
            w = 1;
            v = 0;
        }

        if(a[u][v])
        {
            u =  a[u][v];
            ans += 1 << i;
        }
        else
        {
            u = a[u][w];
            //if(!v)
                //ans += 1 << i;
        }
    }
    return ans;
}
int main()
{
    int t,x;
    cin >> t;
    char s[20];
    Init();
    Insert(0);
    for(int ca = 1;ca <= t;ca++)
    {
        scanf("%s%d",s,&x);
        if(s[0] == '+')
            Insert(x);
        else if(s[0] == '-')
            Delete(x);
        else
            printf("%d\n",Query(x));
    }
    return 0;
}

题目:LA-3942
题意:给你一个T串,然后输入n个P串,让你求将T串分解成若干个P串的方案数
思路:dp+Trie
dp[i]代表从i开始的字符串分解方案数,所以状态转移方程为dp[i] = (dp[i] + dp[i + len[P])
代码:

#include<bits/stdc++.h>
using namespace std;


const int N = 400005;
const int C_size = 26;

const int MOD = 20071027;
vector<int> ans;
int len[N],dp[N];

struct Trie{
    int trieN;
    int ch[N][C_size], val[N];
    void init(){
        memset(ch[0],0,sizeof(ch[0]));
        trieN = -1;
        newnode();
    }
    int newnode(){
        memset(ch[++trieN], 0, sizeof(ch[0]));
        val[trieN] = 0;//fail[trieN] = 0;
        return trieN;
    }
    void insert(char *str,int index){
        int cur = 0;
        for(int i = 0;str[i] != '\0';i++){
            int d = str[i] - 'a';
            if(!ch[cur][d])
                ch[cur][d] = newnode();
            cur = ch[cur][d];
        }
        val[cur] = index;//×Ö·û´®±àºÅ
    }
    void query(char *str,int len){
        int cur = 0;
        for (int i = 0;i < len;i++){
            int d = str[i] - 'a';
            if(!ch[cur][d])
                return;
            cur = ch[cur][d];
            if(val[cur]) ans.push_back(val[cur]);
        }
        //return val[cur];
    }
}trie;

char str[300005],s[4005];
int main(){
    int n,ca = 0;
    while(~scanf("%s",&str)){
        scanf("%d",&n);
        trie.init();
        for(int i = 1;i <= n;i++){
            scanf("%s",s);
            len[i] = strlen(s);
            trie.insert(s,i);
        }
        memset(dp,0,sizeof(dp));
        int ls = strlen(str);
        dp[ls] = 1;
        for(int i = ls-1;i >= 0;i--)
        {
            ans.clear();
            trie.query(str+i,ls-i);
            for(int j = 0;j < ans.size();j++)
                dp[i] = (dp[i] + dp[i + len[ans[j]]]) % MOD;
        }
        printf("Case %d: %d\n",++ca,dp[0]);
    }
    return 0;
}

2.KMP

http://blog.csdn.net/huatian5/article/details/51307735

3.Aho-Corasick自动机(AC自动机)

题目:http://acm.split.hdu.edu.cn/showproblem.php?pid=2222
题意:给你n个P串,再给你一个T串,让求T中有多少P串
思路:AC自动机
代码:

#include<bits/stdc++.h>
using namespace std;

const int N = 1e6+5;
const int C_size = 26;
char str[N];
struct Trie{
    const static int maxn = N;
    const static int type = C_size;
    int ch[maxn][type],fail[maxn],val[maxn];
    int root,L;
    int newnode(){
        for(int i = 0;i < type;i++)
            ch[L][i] = -1;
        val[L++] = 0;
        return L-1;
    }
    void init(){
        L = 0;
        root = newnode();
    }
    void insert(char* str){
        int len = strlen(str);
        int now = root;
        for(int i = 0;str[i];i++){
            int id=str[i]-'a';
            if(ch[now][id] == -1)
                ch[now][id] = newnode();
            now=ch[now][id];
        }
        val[now]++;
    }
    void build(){
        queue<int>Q;
        fail[root] = root;
        for(int i = 0;i < type;i++)
            if(ch[root][i] == -1)
                ch[root][i] = root;
            else{
                fail[ch[root][i]] = root;
                Q.push(ch[root][i]);
            }
        while(!Q.empty()){
            int now = Q.front();Q.pop();
            for(int i = 0;i < type;i++)
                if(ch[now][i] == -1)
                    ch[now][i] = ch[fail[now]][i];
                else{
                    fail[ch[now][i]] = ch[fail[now]][i];
                    Q.push(ch[now][i]);
                }
        }
    }
    int query(char *str){
        int ans = 0;
        int now = root;
        for(int i = 0;str[i];i++){
            now = ch[now][str[i]-'a'];
            int tmp = now;
            while(tmp != root){
                ans += val[tmp];
                val[tmp] = 0;
                tmp = fail[tmp];
            }
        }
        return ans;
    }
}AC;
int main()
{
    int t,n;
    scanf("%d",&t);
    while(t--)
    {
        AC.init();
        scanf("%d",&n);
        for(int i = 1;i <= n;i++)
        {
            scanf("%s",str);
            AC.insert(str);
        }
        AC.build();
        getchar();
        gets(str);
        printf("%d\n",AC.query(str));
    }
    return 0;
}

题目:http://acm.split.hdu.edu.cn/showproblem.php?pid=5880
题意:给你n个P串,再给你一个T串,让你将T串中的P串改为“*”
思路:AC自动机,匹配到P串后,在串尾标记下len,输出的时候判断下len就行了
代码:

#include<bits/stdc++.h>
using namespace std;

const int N = 1e6+5;
const int C_size = 26;
char str[N];
int pos[N];
struct Trie{
    const static int maxn = N;
    const static int type = C_size;
    int ch[maxn][type],fail[maxn],val[maxn],l[maxn];
    int root,L;
    int newnode(){
        for(int i = 0;i < type;i++)
            ch[L][i] = -1;
        l[L] = 0;
        val[L++] = -1;
        return L-1;
    }
    void init(){
        L = 0;
        root = newnode();
    }
    void insert(char* str){
        int len = strlen(str);
        int now = root;
        for(int i = 0;str[i];i++){
            int id=str[i]-'a';
            if(ch[now][id] == -1)
                ch[now][id] = newnode();
            now=ch[now][id];
        }
        val[now]=1;
        l[now]=len;
    }
    void build(){
        queue<int>Q;
        fail[root] = root;
        for(int i = 0;i < type;i++)
            if(ch[root][i] == -1)
                ch[root][i] = root;
            else{
                fail[ch[root][i]] = root;
                Q.push(ch[root][i]);
            }
        while(!Q.empty()){
            int now = Q.front();
            Q.pop();
            for(int i = 0;i < type;i++)
                if(ch[now][i] == -1)
                    ch[now][i] = ch[fail[now]][i];
                else{
                    fail[ch[now][i]] = ch[fail[now]][i];
                    Q.push(ch[now][i]);
                }
        }
    }
    void solve(char* str){
        int id;
        int len = strlen(str);
        int now = root;
        memset(pos,0,sizeof(pos));
        for(int i = 0;i < len;i++){
            if(str[i]>='A'&&str[i]<='Z') id=str[i]-'A';
            else if(str[i]>='a'&&str[i]<='z') id=str[i]-'a';
            else continue;
            now = ch[now][id];
            int temp=now;
            while(temp != root){
                if(val[temp] != -1){
                    pos[i+1] -= 1;
                    pos[i-l[temp]+1] += 1;
                    break;
                }
                temp = fail[temp];
            }
        }
        long long cnt = 0;
        for(int i=0;i<len;i++){
            cnt += pos[i];
            if(cnt <= 0)
                printf("%c",str[i]);
            else
                printf("*");
        }
        printf("\n");
    }
}AC;
int main()
{
    int t,n;
    scanf("%d",&t);
    while(t--)
    {
        AC.init();
        scanf("%d",&n);
        for(int i = 1;i <= n;i++)
        {
            scanf("%s",str);
            AC.insert(str);
        }
        AC.build();
        getchar();
        gets(str);
        AC.solve(str);
    }
    return 0;
}

题目:http://acm.split.hdu.edu.cn/showproblem.php?pid=6208
题意:给你n个串,让找出最长的一个串,包含其余n-1个串,没有输出No
思路:用AC自动机 找出最长的串,看是否其余n-1个串都能匹配到
代码(来自某好心人):

#include<bits/stdc++.h>
using namespace std;

const int N = 100005;
const int M = 26;
queue<int> q;
vector<string> vec;
bool vis[N];
struct Trie{
    int trieN;
    int ch[N][M], val[N], fail[N];
    void init() {
        memset(vis,0,sizeof(vis));
        trieN = -1;
        newnode();
    }
    int newnode() {
        memset(ch[++trieN], 0, sizeof(ch[0]));
        val[trieN] = fail[trieN] = 0;
        return trieN;
    }
    void insert(const string &str, int index) {
        int cur = 0;
        for (int i = 0;str[i];i++) {
            int d = str[i] - 'a';
            if (!ch[cur][d])
                ch[cur][d] = newnode();
            cur = ch[cur][d];
        }
        if (val[cur]) vis[index] = 1;
        else val[cur] = index;
    }
    void build() {
        for (int i = 0;i < M;i++) {
            if (ch[0][i])
                q.push(ch[0][i]);
        }
        while (!q.empty()) {
            int cur = q.front(); q.pop();
            for (int i = 0;i < M;i++) {
                int &next = ch[cur][i];
                if (next) {
                    fail[next] = ch[fail[cur]][i];
                    q.push(next);
                }
                else next = ch[fail[cur]][i];
            }
        }
    }
    void query(const string &str) {
        int cur = 0, tmp;
        for (int i = 0;str[i];i++) {
            int d = str[i] - 'a';
            tmp = cur = ch[cur][d];
            while (tmp && ~val[tmp]) {
                if (val[tmp] != -1) vis[val[tmp]] = 1;
                val[tmp] = -1;
                tmp = fail[tmp];
            }
        }
    }
}ac;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin >> t;
    while(t--){
        ac.init();
        int n;
        cin >> n;
        vec.resize(n+1);
        int maxlen = 0,maxi;
        for(int i = 1;i <= n;i++){
            cin >> vec[i];
            if (vec[i].size() > maxlen){
                maxlen = vec[i].size();
                maxi = i;
            }
        }
        vis[maxi] = 1;
        for(int i = 1;i <= n;i++){
            if (i != maxi)
                ac.insert(vec[i],i);
        }
        ac.build();
        ac.query(vec[maxi]);
        bool flag = 1;
        for(int i = 1;i <= n && flag;i++){
            if (!vis[i]) flag = 0;
        }
        if(flag) cout << vec[maxi] << '\n';
        else cout << "No\n";
    }
    return 0;
}

4.后缀数组(SA)

  • Suffix[i] :Str下标为i ~ Len的连续子串(即后缀)
  • Rank[i] : Suffix[i]在所有后缀中的排名
  • sa : 满足Suffix[sa1] < Suffix[sa2] …… < Suffix[sa[Len]],即排名为i的后缀为Suffix[sa[i]] (与Rank是互逆运算)
  • height[i] : 表示Suffix[sa[i]]和Suffix[sa[i - 1]]的最长公共前缀,也就是排名相邻的两个后缀的最长公共前缀

题目:poj2774
题意:给你两个子串,让你求最长公共子串的长度
思路:将两个字符串连接(中间加一个非字母的分隔符号),然后保证两个后缀分别在两个串中,即sa[i-1]>=0 && sa[i-1]<len1 && sa[i]>=len1 || sa[i]>=0 && sa[i]<len1 && sa[i-1]>=len1
然后结果就是最大的height[i]
代码:

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int Max = 200005;

int wa[Max],wb[Max],wsf[Max],wv[Max],sa[Max];
int Rank[Max],height[Max],s[Max];
char str1[Max],str2[Max];

int cmp(int *r,int a,int b,int k)
{
    return r[a]==r[b]&&r[a+k]==r[b+k];
}
//±¶ÔöO(nlogn)
void getsa(int *r,int *sa,int n,int m)
{
    int i, j, p, *x = wa, *y = wb;
    for (i = 0; i < m; ++i) wsf[i] = 0;
    for (i = 0; i < n; ++i) wsf[x[i]=r[i]]++;
    for (i = 1; i < m; ++i) wsf[i] += wsf[i-1];
    for (i = n-1; i >= 0; --i) sa[--wsf[x[i]]] = i;
    for (j = 1, p = 1; p < n; j *= 2, m = p)
    {
        for (p = 0, i = n - j; i < n; ++i) y[p++] = i;
        for (i = 0; i < n; ++i) if (sa[i] >= j) y[p++] = sa[i] - j;
        for (i = 0; i < n; ++i) wv[i] = x[y[i]];
        for (i = 0; i < m; ++i) wsf[i] = 0;
        for (i = 0; i < n; ++i) wsf[wv[i]]++;
        for (i = 1; i < m; ++i) wsf[i] += wsf[i-1];
        for (i = n-1; i >= 0; --i) sa[--wsf[wv[i]]] = y[i];
        for (swap(x, y), p = 1, x[sa[0]] = 0, i = 1; i < n; ++i)
            x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p-1 : p++;
    }
}
void calheight(int r[], int n)
{
    int i, j, k = 0;
    for (i = 1; i <= n; ++i) Rank[sa[i]] = i;
    for (i = 0; i < n; height[Rank[i++]] = k)
        for (k?k--:0, j = sa[Rank[i]-1]; r[i+k] == r[j+k]; k++);
}
int main()
{
    char str1[Max],str[Max];
    while(~scanf("%s",str1))
    {
        scanf("%s",str2);
        int len1=strlen(str1),len2=strlen(str2);
        int num=0;
        for(int i=0;i<len1;i++)
            s[num++]=str1[i]-'a'+1;
        s[num++]=28;
        for(int i=0;i<len2;i++)
            s[num++]=str2[i]-'a'+1;
        s[num]=0;
        getsa(s,sa,num+1,30);
        calheight(s,num);
        int ans=0;
        for(int i=2;i<num;i++)
        {
            if(height[i]>ans)
            {aaaa aaaa
                if(sa[i-1]>=0 && sa[i-1]<len1 && sa[i]>=len1)
                    ans = max(ans,height[i]);
                if(sa[i]>=0 && sa[i]<len1 && sa[i-1]>=len1)
                    ans = max(ans,height[i]);
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

题目:Problem I 失恋的小T
题意:问一个字符串里面不同的子串有有多少
思路:我们知道所有子串的个数,只需要找到相同的子串个数,减掉重复的就行了。对于每个后缀来说,贡献为len-sa[i](sa[i]保存的排名为i的后缀开头位置)。
height[i] 就是排名相邻的两个后缀的最长公共前缀,我们找出最长前缀也就是重复的一部分子串,例如aaaabab,abab和ab的最长公共前缀为ab,重复子串有a、ab,所以每个后缀的贡献为贡献为len-sa[i]-height[i]
代码:

#include <bits/stdc++.h>
using namespace std;
const int Max = 200005;

int wa[Max],wb[Max],wsf[Max],wv[Max],sa[Max];
int ra[Max],height[Max],s[Max];
char str[Max];

int cmp(char *r,int a,int b,int k)
{
    return r[a]==r[b]&&r[a+k]==r[b+k];
}
//倍增O(nlogn)
void getsa(char *r,int *sa,int n,int m)
{
    int i,j,p,*x=wa,*y=wb,*t;
    for(i=0;i<m;i++)  wsf[i]=0;
    for(i=0;i<n;i++)  wsf[x[i]=r[i]]++;
    for(i=1;i<m;i++)  wsf[i]+=wsf[i-1];
    for(i=n-1;i>=0;i--)  sa[--wsf[x[i]]]=i;
    /***********************************/
    for(p=1,j=1;p<n;j*=2,m=p)
    {
        for(p=0,i=n-j;i<n;i++)  y[p++]=i;
        for(i=0;i<n;i++)  if(sa[i]>=j)  y[p++]=sa[i]-j;
        for(i=0;i<n;i++)  wv[i]=x[y[i]];
        for(i=0;i<m;i++)  wsf[i]=0;
        for(i=0;i<n;i++)  wsf[wv[i]]++;
        for(i=1;i<m;i++)  wsf[i]+=wsf[i-1];
        for(i=n-1;i>=0;i--)  sa[--wsf[wv[i]]]=y[i];
        for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
            x[sa[i]]=cmp(y,sa[i-1],sa[i],j)? p-1:p++;
    }
    return;
}
void getheight(char *r,int n)
{
    int i,j,k=0;
    for(i=1;i<=n;i++)  ra[sa[i]]=i;
    for(i=0;i<n;i++)
    {
        if(k)
            k--;
        else
            k=0;
        j=sa[ra[i]-1];
        while(r[i+k]==r[j+k])
            k++;
        height[ra[i]]=k;
    }
}

int main()
{
    while(~scanf("%s",str))
    {
        int len = strlen(str);
        getsa(str,sa,len+1,130);
        getheight(str,len);
        int res = 0;
        for(int i = 1;i <= len;i++)
            res += len - sa[i] - height[i];
        printf("%d\n",res);
    }
}

题目:http://acm.split.hdu.edu.cn/showproblem.php?pid=6194
题意:给你一个字符串,让求出现k次的子串个数
思路:我们枚举每个sa[i](长度k),suffix[ i~i+k-1 ]的的lcp(min{height[i]}),假设为s(长度为len),那么有len个子串至少出现了k次,因为题目要求是出现k次,然后我们要减去至少出现了k+1次的子串,即suffix[ i-1~i+k-1]和suffix[ i~i+k]的lcp,容斥加上suffix[ i-1~i+k]的lcp
求height[]的最小值用rmq处理
代码:

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int Max = 200005;

int wa[Max],wb[Max],wsf[Max],wv[Max],sa[Max];
int rankk[Max],height[Max];
char str[Max];

int cmp(int *r,int a,int b,int k)
{
    return r[a]==r[b]&&r[a+k]==r[b+k];
}
void getsa(int *r,int *sa,int n,int m)
{
    int i, j, p, *x = wa, *y = wb;
    for (i = 0; i < m; ++i) wsf[i] = 0;
    for (i = 0; i < n; ++i) wsf[x[i]=r[i]]++;
    for (i = 1; i < m; ++i) wsf[i] += wsf[i-1];
    for (i = n-1; i >= 0; --i) sa[--wsf[x[i]]] = i;
    for (j = 1, p = 1; p < n; j *= 2, m = p)
    {
        for (p = 0, i = n - j; i < n; ++i) y[p++] = i;
        for (i = 0; i < n; ++i) if (sa[i] >= j) y[p++] = sa[i] - j;
        for (i = 0; i < n; ++i) wv[i] = x[y[i]];
        for (i = 0; i < m; ++i) wsf[i] = 0;
        for (i = 0; i < n; ++i) wsf[wv[i]]++;
        for (i = 1; i < m; ++i) wsf[i] += wsf[i-1];
        for (i = n-1; i >= 0; --i) sa[--wsf[wv[i]]] = y[i];
        for (swap(x, y), p = 1, x[sa[0]] = 0, i = 1; i < n; ++i)
            x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p-1 : p++;
    }
}
void calheight(int r[], int n)
{
    int i, j, k = 0;
    for (i = 1; i <= n; ++i) rankk[sa[i]] = i;
    for (i = 0; i < n; height[rankk[i++]] = k)
        for (k?k--:0, j = sa[rankk[i]-1]; r[i+k] == r[j+k]; k++);
}
int len,a[Max],d[Max][40];
void rmq_init(int* A, int n)
{
    for(int i = 0; i < n; ++i)
        d[i][0] = A[i];
    for(int j = 1; (1<<j) <= n; ++j)
        for(int i = 0; i + (1<<j) - 1 < n ; ++i)
            d[i][j] = min(d[i][j-1], d[i + (1<< (j-1))][j-1]);
}

int ASK(int l,int r)
{
    int k = 0;
    while((1<<(k+1)) <= r-l + 1)
        ++k;
    return min(d[l][k], d[r-(1<<k) + 1][k]);
}

int ask(int l,int r)
{
    if (l == r)
        return len - sa[r];
    return ASK(l + 1, r);
}
int main()
{
    int T,k;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&k);
        scanf("%s",str);
        len = strlen(str);
        for(int i = 0;i < len;i++)
            a[i] = str[i]-'a'+1;
        a[len] = 0;
        getsa(a,sa,len+1,30);
        calheight(a,len);
        rmq_init(height,len+1);

        ll ans = 0;
        for(int i = 1;i + k - 1 <= len;i++)
        {
            ans += ask(i,i+k-1);
            if(i-1 > 0)
                ans -= ask(i-1,i+k-1);
            if(i+k <= len)
                ans -= ask(i,i+k);
            if(i-1 > 0 && i+k <= len)
                ans += ask(i-1,i+k);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

哈希

待续……

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值