字符串板子

KMP

nxt:前缀函数,nxt[i]表示子串ss[0,...,i]最长的相等的真前缀与真后缀的长度.

vector<int> kmp(const string &ss)
{
    int len = ss.size();
    vector<int> nxt(len, 0);
    for(int i = 1, j = 0; i < len; i++)
    {
        while(j > 0 && ss[i] != ss[j])
            j = nxt[j - 1];
        nxt[i] = (ss[i] == ss[j] ? ++j : j);
    }
    return nxt;
}

扩展KMP(Z函数)

z[i]的意义,以0开始的后缀与以i开始的后缀的最长公共前缀的长度

vector<int> z_function(const string& ss)
{
    int len = (int)ss.size();
    vector<int> z(len, 0);
    for(int i = 1, l = 0, r = 0; i < len; i++)//切记不可将i设为0
    {
        if(i <= r)
            z[i] = min(r - i + 1, z[i - l]);
        while(i + z[i] < len && ss[z[i]] == ss[i + z[i]])
            z[i]++;
        if(i + z[i] - 1 > r)
        {
            l = i;
            r = i + z[i] - 1;
        }
    }
    //看需求使用
    // z[0] = len;
    return z;
}

trie树

insert插入字符串

find查询字符串是否出现过

struct Trie{
    enum{ alpha = 26, cr = 'a' };
    struct node{
        array<int,26> ch;
        bool is_leaf;
        bool dian;
        node()
        {
            is_leaf = false;
            dian = false;
            fill(ch.begin(), ch.end(), 0);
        }
    };
    vector<node> tr;
    int new_node()
    {
        tr.emplace_back();
        return (int)tr.size() - 1;
    }
    Trie() { new_node(); }
    void insert(const string &ss)
    {
        int p = 0;
        for(char c : ss)
        {
            if(!tr[p].ch[c - cr])
                tr[p].ch[c - cr] = new_node();
            p = tr[p].ch[c - cr];
        }
        tr[p].is_leaf = true;
    }
    bool find(const string &ss)
    {
        int p = 0;
        for(char c : ss)
        {
            if(!tr[p].ch[c - cr])
                return false;
            p = tr[p].ch[c - cr];
        }
        return tr[p].is_leaf;
    }
};

前缀自动机

Prefix Function Queries

/*
 author:wuzx
 */

#include<bits/stdc++.h>
#define ll long long
#define int long long
#define endl "\n"
#define P pair<int,int>
#define f first
#define s second
using namespace std;
const int inf = 0x3f3f3f3f;
int t;
int n,m,k;
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    string ss;
    cin >> ss;
    n = ss.size();
    vector<int> fail(n + 10);
    vector<array<int,26>> ch(n + 10);
    ch[0][ss[0] - 'a'] = 1;
    // fail[1] = 0;
    for(int i = 1; i < n; i++)
    {
        for(int j = 0; j < 26; j++)
        {
            if(j == ss[i] - 'a')
            {
                ch[i][j] = i + 1;
                fail[i] = ch[fail[i - 1]][j];
            }
            else
                ch[i][j] = ch[fail[i - 1]][j];
        }
        // cout << fail[i] << " \n"[i == n - 1];
    }
    int q;
    cin >> q;
    while(q--)
    {
        string s1;
        cin >> s1;
        m = s1.size();
        for(int i = n; i < n + m; i++)
        {
            for(int j = 0; j < 26; j++)
            {
                if(j == s1[i - n] - 'a')
                {
                    ch[i][j] = i + 1;
                    fail[i] = ch[fail[i - 1]][j];
                }
                else
                    ch[i][j] = ch[fail[i - 1]][j];
            }
            cout << fail[i] << " \n"[i == n + m - 1];
        }
    }
    return 0;
}

马拉车

mlc[i]表示以i为中心的最长回文串半径的长度

vector<int> manacher(const string &s1)
{
    string ss;
    for(char c : s1)
        ss += '#', ss += c;
    ss += '#';
    int len = (int)ss.size();
    vector<int> mlc(len);
    for(int i = 0, l = 0, r = -1; i < len; i++)
    {
        int kk = (i > r) ? 1 : min(mlc[l + r - i], r - i);
        while(0 <= i - kk && i + kk < len && ss[i - kk] == ss[i + kk])
            kk++;
        mlc[i] = kk--;
        if(i + kk > r)
        {
            l = i - kk;
            r = i + kk;
        }
    }
    int ma = 0, idx;
    for(int i = 0; i < len; i++)
    {
        if(mlc[i] > ma)
        {
            ma = mlc[i];
            idx = i;
        }
    }
    for(int i = 0; i < len; i++)
        cout << ss[i] << " \n"[i == len - 1];
    for(int i = 0; i < len; i++)
        cout << mlc[i] << " \n"[i == len - 1];
    string s2 = ss.substr(idx - ma + 1, 2 * ma - 1);
    string lstr;
    for(char c : s2)
        if(c != '#')
            lstr += c;
    cout << lstr << endl;
    return mlc;
}

AC自动机:

/*
 author:wuzx
 */
 
#include<bits/stdc++.h>
#define ll long long
#define endl "\n"
#define P pair<int,int>
#define f first
#define s second
using namespace std;
typedef unsigned long long ull;
const int maxn=200010;
const int inf=0x3f3f3f3f;
const int mod=998244353;
int t;
int n,m,k;
struct automaton{
    enum {alpha = 26, cr = 'a'};
    int id;
    using ic = array<int, alpha >;
    vector<ic> tr;
    vector<int> back,end,cnt,output;
    automaton():id(0),tr(1),back(1,0),end(1,-1),cnt(1,0),output(1,-1)
    {
        fill(tr[id].begin(),tr[id].end(),-1);
    }
    void add(int v)
    {
        tr.emplace_back(ic());
        fill(tr[id].begin(),tr[id].end(),v);
        cnt.emplace_back(0);
        back.emplace_back(0);
        output.emplace_back(-1);
        end.emplace_back(-1);
    }
    void insert(string &ss,int j)// j: id of string s
    {
        int p=0,len=ss.length();
        for(int i=0;i<len;i++)
        {
            int x=ss[i]-cr;
            if(tr[p][x]==-1)
            {
                tr[p][x]=++id;
                add(-1);
            }
            p=tr[p][x];
        }
        end[p]=j;
        cnt[p]++;
    }
    void build()
    {
        back[0]=++id;
        add(0);
        queue<int> q;
        q.push(0);
        while(!q.empty())
        {
            int p=q.front();
            q.pop();
            for(int i=0;i<alpha;i++)
            {
                int pnx=tr[back[p]][i];
                int now=tr[p][i];
                if(tr[p][i]==-1)
                    tr[p][i]=pnx;
                else
                {
                    back[now]=pnx;
                    output[now]=end[pnx]==-1?output[pnx]:pnx;
                    q.push(tr[p][i]);
                }
            }
        }
    }
    // for each position, finds the longest pattern that ends here
    vector<int> find(const string &ss)
    {
        int len=ss.length();
        vector<int> res(len);
        int p=0;
        for(int i=0;i<len;i++)
        {
            p=tr[p][ss[i]-cr];
            res[i]=end[p];
        }
        return res;
        //返回的数组表示ss串里以第i个位置字符结尾的串的编号
    }
    // for each position, finds the all that ends here
    vector<vector<int>> find_all(const string &ss)
    {
        int len=ss.length();
        vector<vector<int>> res(len);
        int p=0;
        for(int i=0;i<len;i++)
        {
            p=tr[p][ss[i]-cr];
            res[i].push_back(end[p]);
            for(int ind=output[p];ind!=-1;ind=output[ind])
                res[i].push_back(end[ind]);
        }
        return res;
    }
    //set<int>
    int find_cnt(const string &ss,int nn)//输入待查询字符串及插入字符串个数,返回待查询字符串中有多少个插入字符串
    {
        int len=ss.length();
        vector<int> num(nn,0);
        int p=0,ans=0;
        for(int i=0;i<len;i++)
        {
            p=tr[p][ss[i]-cr];
            if(end[p]!=-1)
            {
                if(!num[end[p]])
                {
                    num[end[p]]++;
                    ans+=cnt[p];
                }
            }
            for(int ind=output[p];ind!=-1;ind=output[ind])
            {
                if(!num[end[ind]])
                {
                    num[end[ind]]++;
                    ans+=cnt[ind];
                }
            }
        }
        return ans;
    }
    vector<int> find_maxcnt(const string &ss,int nn)//返回出现次数最多的字符串的下标
    {
        int len=ss.length();
        vector<int> num(nn,0);
        int p=0,ans=0;
        for(int i=0;i<len;i++)
        {
            p=tr[p][ss[i]-cr];
            if(end[p]!=-1)
            {
                num[end[p]]++;
                ans=max(ans,num[end[p]]);
            }
            for(int ind=output[p];ind!=-1;ind=output[ind])
            {
                num[end[ind]]++;
                ans=max(ans,num[end[ind]]);
            }
        }
        cout<<ans<<endl;
        vector<int> idx;
        for(int i=0;i<nn;i++)
            if(num[i]==ans)
                idx.push_back(i);
        return idx;
    }
};
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    cin>>t;
    while(t--)
    {   
        int n;
        cin>>n;
        automaton ac1;
        string s1[n],ss;
        for(int i=0;i<n;i++)
        {
            cin>>s1[i];
            ac1.insert(s1[i],i);
        }
        ac1.build();
        cin>>ss;
        cout<<ac1.find_cnt(ss,n)<<endl;
    }
    return 0;
}

字符串哈希:

struct string_hash{
    const int nn;
    const ll Base1 = 29, MOD1 = 1e9 + 7;
    const ll Base2 = 131, MOD2 = 1e9 + 9;
    vector<ll> ha1, ha2, pow1, pow2;
    vector<ll> rha1, rha2;
    string_hash(const string &ss,int n1) : nn(n1), ha1(nn + 1), ha2(nn + 1), pow1(nn + 1), pow2(nn + 1), rha1(nn + 1), rha2(nn + 1)
    {
        pow1[0] = pow2[0] = 1;
        for(int i = 1; i <= nn; i++)
        {
            pow1[i] = pow1[i - 1] * Base1 % MOD1;
            pow2[i] = pow2[i - 1] * Base2 % MOD2;
        }
        for(int i = 1; i <= nn; i++)
        {
            ha1[i] = (ha1[i - 1] * Base1 + ss[i - 1]) % MOD1;
            ha2[i] = (ha2[i - 1] * Base2 + ss[i - 1]) % MOD2;
            rha1[i] = (rha1[i - 1] * Base1 + ss[nn - i]) % MOD1;
            rha2[i] = (rha2[i - 1] * Base2 + ss[nn - i]) % MOD2;
        }
    }
    pair<ll, ll> get_hash(int l,int r)
    {
        ll res1 = ((ha1[r] - ha1[l - 1] * pow1[r - l + 1]) % MOD1 + MOD1) % MOD1;
        ll res2 = ((ha2[r] - ha2[l - 1] * pow2[r - l + 1]) % MOD2 + MOD2) % MOD2;
        return {res1, res2};
    }
    pair<ll, ll> get_rhash(int l, int r)
    {
        ll res1 = ((rha1[n - l + 1] - rha1[n - r] * pow1[r - l + 1]) % MOD1 + MOD1) % MOD1;
        ll res2 = ((rha2[n - l + 1] - rha2[n - r] * pow2[r - l + 1]) % MOD2 + MOD2) % MOD2;
        return {res1, res2};
    }
    bool is_palindrome(int l, int r)//判断ss[l, r]是否为回文串
    {
        return get_hash(l, r) == get_rhash(l, r);
    }
    pair<ll, ll> add(pair<ll, ll> aa,pair<ll, ll> bb)
    {
        ll res1 = (aa.f + bb.f) % MOD1;
        ll res2 = (aa.s + bb.s) % MOD2;
        return {res1, res2};
    }
    pair<ll, ll> mul(pair<ll, ll> &aa, ll kk) //aa *= Base的k次方
    {
        ll res1 = aa.f * pow1[kk] % MOD1;
        ll res2 = aa.s * pow2[kk] % MOD2;
        return {res1, res2};
    }
};

后缀数组SA

倍增排序O(nlog^2n)

pair<vector<int>,vector<int>> SA(string &ss,int len)
{
    ss = '#' + ss;
    vector<int> sa(len << 1|1),rk(len << 1|1);
    for(int i = 1; i <= len ;i++)
    {
        sa[i] = i;
        rk[i] = ss[i];
    }

    for(int w = 1; w < len; w <<= 1)
    {
        sort(sa.begin() + 1, sa.begin() + 1 + len, [&](int x, int y)
        {
            return rk[x] == rk[y] ? rk[x + w] < rk [y + w] : rk[x] < rk[y];
        });
        auto oldrk = rk;
        for(int p = 0, i = 1; i <= len; i++)
        {
            if(oldrk[sa[i]] == oldrk[sa[i - 1]] &&
             oldrk[sa[i] + w] == oldrk[sa[i - 1] + w])
                rk[sa[i]] = p;
            else
                rk[sa[i]] = ++p;
        }
    }

    for(int i = 1; i <= len; i++)
        cout << sa[i] << " ";
    cout << endl;
    return make_pair(sa,rk);
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    string ss;
    cin >> ss;
    int len = ss.size();
    auto [sa,rk] = SA(ss,len);
    return 0;
}

计数排序O(nlogn)

void SA(string &ss,int len,vector<int> &sa,vector<int> &rk,vector<int> &height)
{
    ss = '#' + ss;
    int mm = max(len,300);//串串题先看要不要开ll
    sa.assign(len << 1|1,0);
    rk.assign(len << 1|1,0);
    height.assign(len + 1,0);
    vector<int> cnt(mm + 1,0),id(len + 1,0),px(len + 1,0);
    for(int i = 1; i <= len; i++)
        ++cnt[rk[i] = ss[i]];
    for(int i = 1; i <= mm; i++)
        cnt[i] += cnt[i - 1];
    for(int i = len; i >= 1; i--)
        sa[cnt[rk[i]]--] = i;
    int i,p;
    for(int w = 1; ; w <<= 1 , mm = p)
    {
        for(p = 0,i = len; i > len - w; i--)
            id[++p] = i;
        for(i = 1; i <= len; i++)
            if(sa[i] > w)
                id[++p] = sa[i] - w;
        cnt.assign(mm+1,0);
        for(i = 1; i <= len; i++)
            ++cnt[px[i] = rk[id[i]]];
        for(i = 1; i <= mm; i++)
            cnt[i] += cnt[i - 1];
        for(i = len; i >= 1; i--)
            sa[cnt[px[i]]--] = id[i];
        auto oldrk = rk;
        auto cmp = [&](int x,int y,int ww)
        {
            return oldrk[x] == oldrk[y] && oldrk[x + ww] == oldrk[y + ww];
        };
        for(p = 0, i = 1; i <= len; i++)
            rk[sa[i]] = cmp(sa[i],sa[i - 1],w) ? p : ++p;
        if(p == len)
        {
            for(i = 1; i <= len; i++)
                sa[rk[i]] = i;
            break;
        }
    }
    for(int i = 1; i <= len; i++)
        cout << sa[i] << " ";
    cout << endl;
    for(int i = 1, h = 0; i <= len; i++)
    {
        if(rk[i] == 0)
            continue;
        h = h != 0 ? --h : 0;
        while(ss[i + h] == ss[sa[rk[i] - 1] + h])
            ++h;
        height[rk[i]] = h;
    }
    return ;
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    string ss;
    cin >> ss;
    int len = ss.size();
    vector<int> sa,rk,height;
    SA(ss,len,sa,rk,height);
    return 0;
}

后缀自动机

struct SAM{
    enum{alpha = 26, ch = 'a'};
    struct state{
        int len = 0,link = -1;
        array<int,alpha> nxt;
        state()
        {
            len = 0;
            link = -1;
            fill(nxt.begin(),nxt.end(),0);
        }
    };
    int last = 0;
    vector<state> st;
    void extend(char c)
    {
        int cur = (int)st.size();
        st.emplace_back();
        st[cur].len = st[last].len + 1;
        int p = last;
        while(p != -1 &&!st[p].nxt[c - ch])
        {
            st[p].nxt[c - ch] = cur;
            p = st[p].link;
        }
        if(p == -1)
            st[cur].link = 0;
        else
        {
            int q = st[p].nxt[c-ch];
            if(st[p].len + 1 == st[q].len)
                st[cur].link = q;
            else
            {
                int clone = (int)st.size();
                st.push_back(st[q]);
                st[clone].len = st[p].len + 1;
                while(p != -1 && st[p].nxt[c - ch] == q)
                {
                    st[p].nxt[c - ch] = clone;
                    p = st[p].link;
                }
                st[q].link = st[cur].link = clone;
            }
        }
        last = cur;
    }
    SAM() {st.emplace_back();}
    SAM(const string &ss): SAM()
    {
        for(char c:ss)
            extend(c);
    }
    bool find(string &s1)//查询子串
    {
        int now=0;
        for(auto c:s1)
        {
            if(st[now].nxt[c- ch])
                now=st[now].nxt[c - ch];
            else
                return false;
        }
        return true;
    }
    long long cal()//不同子串个数
    {
        int nn=(int)st.size();
        long long ans = 0;
        for(int i = 1; i < nn; i++)
            ans += st[i].len - st[st[i].link].len;
        return ans;
    }
};

回文自动机(回文树)

struct PAM{
    enum{ alpha = 26, cr = 'a'};
    int sz, tot, last;
    string ss;
    struct node{
        int cnt, fail, len;
        array<int, alpha> ch;
        node()
        {
            cnt = 0, fail = 0;
            fill(ch.begin(), ch.end(), 0);
        }
    };
    vector<node> st;
    int new_node(int l)
    {
        sz++;
        st.emplace_back();
        st[sz].len = l;
        return sz;
    }
    void init()//初始化要先init,再一个个insert
    {
        sz = -1;
        last = 0;
        tot = 0 ;
        ss = '$';
        new_node(0);
        new_node(-1);
        st[0].fail = 1;
    }
    int getfail(int x)
    {
        while(ss[tot - st[x].len - 1] != ss[tot])
            x = st[x].fail;
        return x;
    }
    void insert(char c)
    {
        ss += c;
        tot++;
        int now = getfail(last);
        if(!st[now].ch[c - cr])
        {
            int x = new_node(st[now].len + 2);
            st[x].fail = st[getfail(st[now].fail)].ch[c - cr];
            st[now].ch[c - cr] = x;
        }
        last = st[now].ch[c - cr];
        st[last].cnt++;
    }
    PAM(const string &s1)
    {
        init();
        for(char c : s1)
            insert(c);
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值