ACM模板 字符串

11 篇文章 0 订阅
6 篇文章 0 订阅

@(ACM模板)

1. KMP

1. MP算法
  • 此为MP算法,KMP对fail数组进行了优化
  • 对于文本串T和模式串P,判断P是否为T的子串,若是,则返回匹配位置
  • 复杂度 O(n+m) ,其中n和m分别为T和P的长度
  • 若P非T的子串,返回-1
  • 字符数组的下标从0开始,lost的下标从0开始
  • kmp返回值下标从1开始(因为符合通常习惯)
#include<bits/stdc++.h>
namespace KMP
{
    const int maxn = 1e6 + 5;//字符串长度
    int fail[maxn];//fail指针,fail[i]代表i失配后,前面的字符串s[0...i-1]中,满足”前缀等于后缀“的前缀里,最长的前缀的位置加一
    //也就是说,fail指针指向的是失配后“待匹配”的位置

    void getFail(char *P)
    {
        int m = strlen(P);
        fail[0] = fail[1] = 0;
        for(int i = 1; i < m; ++ i)
        //寻找位置i“前缀等于后缀”的最大长度,作为fail[i+1]
        {
            //先不管字符i,找前面的“前缀等于后缀”的最大长度
            int j = fail[i];
            //然后比较位置i
            //重复这个过程直到匹配
            while(j && P[i] != P[j]) j = fail[j];
            //若一直匹配不到,j会逐渐减小到0
            //这时候需要判断一下是否匹配到了
            fail[i + 1] = (P[i] == P[j])? j + 1 : 0;
        }
    }

    int finda(char *T, char *P)//T为文本串,P为模式串
    //有解返回开始位置,无解返回-1
    {
        int n = strlen(T);
        int m = strlen(P);
        getFail(P);
        int j = 0;//模式串的待匹配结点
        for(int i = 0; i < n; ++ i)//文本串当前指针
        {
            while(j && P[j] != T[i]) j = fail[j];//顺着fail指针走,直到可以匹配or走到头
            if(P[j] == T[i]) ++ j;//更新待匹配位置
            if(j == m)//全部都匹配完了
                return i - m + 1 + 1;//返回1-indexed的位置
        }
        return -1;
    }
}
2. KMP算法

待整理。。。。

#include<iostream>  
#include<cstring>  
#include<cstdio>  
#include<algorithm>  
using namespace std;  
#define N 100010  
char str1[N], str2[N];  
int nextval[N];  
int lens, lenp;  

void getnext(const char *p, int nextval[]) //前缀函数(滑步函数)  
{  
    int i = 0, j = -1;  
    nextval[0] = -1;  
    while(i != lenp)  
    {  
        if(j == -1 || p[i] == p[j]) //(全部不相等从新匹配 || 相等继续下次匹配)  
        {  
            //++i,++j之后,再次判断p[i]与p[j]的关系
            ++i, ++j;  
            if(p[i] != p[j]) //abcdabce  
                nextval[i] = j;  
                //next[i] = next[j]; 
                //这里其实是优化了后的,也可以仍是next[i]=j
                //当str[i]==str[j]时,如果str[i]匹配失败,那么换成str[j]肯定也匹配失败,所以不是令next[i]=j,而是next[i] = next[j],跳过了第j个字符,
            //即省去了不必要的比较,优化前的next[i]表示前i个字符中前缀与后缀相同的最大长度
            else //abcabca  
                nextval[i] = nextval[j];  
        }  
        else  
            j = nextval[j]; //子串移动到第nextval[j]个字符和主串相应字符比较  
    }  
    cout<<"前缀函数为:"<<endl;  
    for(int i = 0; i < lenp; ++i)  
        printf("%d", nextval[i]);  
    cout<<endl;  
}  

int KMP(char *s, char *p, int nextval[]) //KMP算法  
{  
    int i = 0, j = 0; //s和j字符串从头开始比较  
    while(i != lens && j != lenp)  
    {  
        if(s[i] == p[j]) //相等继续匹配  
            ++i, ++j;  
        else  
        {  
            if(nextval[j] == -1) //-1代表与p[0]间接比较过,需要主串后移,p重新从头匹配  
                ++i, j = 0;  
            else  
                j = nextval[j]; //直接右移nextval[j]位与s[i]比较  
        }  
    }  
    if(j == lenp) //返回从主串第几个元素开始匹配  
        return i - j;  
    else  
        return -1;  
}  

int main() //主串子串位置从0开始  
{  
    int pos;  
    while(~scanf("%s%s", str1, str2)) //str1为主串,str2为子串  
    {  
        lens = strlen(str1);  
        lenp = strlen(str2);  
        if(lens < lenp) //主串长度<子串长度  
        {  
            printf("主串长度不应小于子串长度!\n");  
            continue;  
        }  
        getnext(str2, nextval); //求子串的前缀函数  
        pos = KMP(str1, str2, nextval);  
        if(pos == -1)  
            printf("主串中不含有子串\n");  
        else  
            printf("子串从主串的第 %d 个元素开始匹配\n", pos);  
    }  
    return 0;  
}
3. KMP求循环节
  1. 注释部分为求前缀循环节
const int maxn = 1e6+5;
int fail[maxn];
char s[maxn];
void getFail(char* P)
{
    int m = strlen(P);
    fail[0] = fail[1] = 0;
    for(int i = 1; i < m; i++)
    {
        int j = fail[i];
        while(j && P[i] != P[j]) j = fail[j];
        fail[i+1] = P[i] == P[j] ? j+1 : 0;
    }
}

int repetend(char* s)
{
    getFail(s);
    int n = strlen(s);
    int len;//循环节长度
    int period;//循环节周期数

//下面三段代码选择一段

    //01. 求该字符串的循环节
    len = n - fail[n];
    period = n/len;    if(n % len== 0) return len;
    else return n;

    //02. 求该字符的前缀的循环节
//    for(int i = 2; i <= n; i++)//考察长度为i的前缀
    {
        len = i - fail[i];//循环节长度
        period = i/len;//循环节周期数
        if(i != len && i % len == 0)
            printf("%d %d %d\n", i, len, period);
    }

    //03. 求该字符最少在结尾补上几个字符,使其成为周期循环字符串,且周期数大于1
    len = n - fail[n];
    if(len != n && n % len== 0) return 0;
    else
        return len - fail[n] % len; //取余的作用:abcab,去掉abc
}
3. 扩展KMP
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;

const int maxn = 1e6 + 5;

char s1[maxn], s2[maxn];

struct ExtendKMP
{
    //模式串P(Pattern)长度m,文本串T(Text)长度n

    //nxt[i]:T[i..n-1]与T的LCP长度
    //extend[i]:P[i..m-1]与T的LCP长度
    int nxt[maxn], extend[maxn];

    void getNext(char *P)
    {
        int m = strlen(P);
        nxt[0] = m;
        int i = 0;
        while(P[i] == P[i+1]) ++i;
        nxt[1] = i;
        int id = 1;
        for(i = 2; i < m; ++ i)
        {
            if(nxt[i-id] + i < id + nxt[id]) nxt[i] = nxt[i-id];
            else
            {
                int j = nxt[id] + id - i;
                if(j < 0) j = 0;
                while(i+j < m && P[j] == P[j+i]) ++j;
                nxt[i] = j;
                id = i;
            }
        }
    }

    void getExtend(char *P, char *T)
    {
        int m = strlen(P);
        int n = strlen(T);
        getNext(T);
        int i = 0;
        while(i < m && i < n && P[i] == T[i]) ++i;
        extend[0] = i;
        int id = 0;
        for(int i = 1; i < m; ++i)
        {
            if(nxt[i-id]+i < extend[id]+id) extend[i] = nxt[i-id];
            else
            {
                int j = extend[id] + id - i;
                if(j < 0) j = 0;
                while(i + j < m && j < n && P[j+i] == T[j]) ++j;
                extend[i] = j;
                id = i;
            }
        }
    }
};

2. hash函数

1. 生成hash函数

下面计算了字符数组的hash值,要求s[l]…s[r]这个字串的hash,用getHash(l,r)即可
h1=s1
h2=s1b1+s2
h3=s1b2+s2b1+s3

hr=s1br1+s2br2++sl1br1+1++sr
hl1=s1bl2+s2bl3++sl1
hrhl1br1+1=slbrl+s2brl1++sr

注意:
- 字符数组下标从1开始

typedef unsigned long long ull;
const int maxn = 1e5+7;
const ull base = 163;
char s[maxn];
ull hah[maxn];
ull pw[maxn];

void calcHash(char* s)
{
    pw[0]  = 1;
    hah[0] = 0;
    int n = strlen(s+1);
    for(int i = 1; i < maxn; i++) pw[i] = pw[i-1] * base;
    for(int i = 1; i <= n; i++) hah[i] = hah[i-1] *  base + s[i];
}
ull getHash(int l, int r)
{
    return hah[r] - hah[l-1] * pw[r-l+1];
}
int main()
{
    scanf("%s", s+1);
    calcHash(s);
    return 0;
}
2. 字符串匹配

1中的代码加上下面的函数

int strMatch(char* T, char* P)
{
    int n = strlen(T+1), m = strlen(P+1);
    initHash(T, hah);
    initHash(P, hah2);
    int h = getHash(1, m, hah2);
    for(int i = 1; i + m - 1 <= n; i++)
        if(getHash(i, i+m-1, hah) == h) return i;
    return -1;
}

int main()
{
    scanf("%s%s", T+1, P+1);//start with 1!!!
    return 0;
}

4. Manacher算法(求最长回文子串)

const int maxn = 1e3+5;
int p[maxn];
string Manacher(string s)
{
    string t = "@#";
    for (int i = 0; i < s.size(); ++i)
    {
        t += s[i];
        t += "#";
    }
    memset(p, 0, sizeof p);
    int mx = 0, id = 0, resLen = 0, resCenter;
    for(int i = 1; i < t.size(); ++i)
    {
        p[i] = mx>i ? min(p[2*id-i], mx-i) : 1;
        while(t[i+p[i]] == t[i-p[i]]) ++p[i];
        if(mx < i+p[i])
        {
            mx = i + p[i];
            id = i;
        }
        if(resLen < p[i])
        {
            resLen = p[i];
            resCenter = i;
        }
    }

    return s.substr((resCenter - resLen) / 2, resLen-1 );
}

5. 后缀数组

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5+7;
char s[maxn];
int sa[maxn], t[maxn], t2[maxn], c[maxn], n;
void build_sa(int m)
{  b 
    int *x = t, *y = t2;
    //index sort
    for(int i = 0; i < m; ++i) c[i] = 0;
    for(int i = 0; i < n; ++i) ++c[x[i] = s[i]];
    for(int i = 1; i < m; ++i) c[i] += c[i-1];
    for(int k = 1; k <= n; k <<= 1)
    {
        int p = 0;
        //直接利用sa数组排序第二关键字
        for(int i = n - k; i < n; ++i)
            if(sa[i] >= k) y[p++] = sa[i] - k;
        //基数排序第一关键字
        for(int i = 0; i < m; ++i) c[i] = 0;
        for(int i = 0; i < n; ++i) ++c[x[y[i]]];
        for(int i = 0; i < m; ++i) c[i] += c[i-1];
        for(int i = n - 1; i >= 0; --i) sa[--c[x[y[i]]]] = y[i];
        //根据sa和y数组计算新的x数组
        swap(x, y);
        p = 1;
        x[sa[0]] = 0;
        for(int i = 1; i < n; ++i)
            x[sa[i]] = (y[sa[i-1]] == y[sa[i]] && y[sa[i-1]+k] == y[sa[i]+k]) ? (p-1) : p++;
        if(p >= n) break;
        m = p;
    }
}

int m;//模板长度。简单起见存为全局变量
int cmp_suf(char *pattern, int p)//判断模板s是否为后缀p的前缀
{
    return strncmp(pattern, s + sa[p], m);
}
int finda(char *P)
{
    m = strlen(P);
    if(cmp_suf(P, 0) < 0 || cmp_suf(P, n-1) > 0) return -1;
    int l = 0, r = n-1;
    while(l <= r)
    {
        int mid = (l + r) >> 1;
        int res = cmp_suf(P, mid);
        if(!res) return mid;
        if(res < 0)
            r = mid - 1;
        else
            l = mid + 1;
    }
    return -1;
}
int main()
{
    return 0;
}

6. Trie树

注意,根据Trie节点中内容的不同(如下面代码中为小写字母),需要注意maxm的不同、字符到id的映射关系不同

const int maxn = 1e5+7;//number of letters
const int maxm = 26;//size of lower case letters

//a Trie of lower case strings
struct Trie
{
    int ch[maxn][maxm];
    int val[maxn];//assume that val is positive
    int tot;//节点总数
    Trie()
    {
        tot = 1;
        memset(ch[0], 0, sizeof ch[0]);
    }

    //insert an string s, whose value is v; note that v != 0. 0 stands for "not an end point"
    void add(char *s, int v)
    {
        int u = 0;//root
        int n = strlen(s);
        for(int i = 0; i < n; ++i)
        {
            int id = s[i] - 'a';
            if(!ch[u][id])//the point does not exist
            {
                memset(ch[tot], 0, sizeof ch[tot]);
                val[tot] = 0;//the val of middle point is 0
                ch[u][id] = tot++;
            }
            u = ch[u][id];
        }
        val[u] = v;
    }
    int finda(char *s)//return -1 if not exists
    {
        int u = 0;//root;
        int n = strlen(s);
        for(int i = 0; i < n; ++i)
        {
            int id = s[i] - 'a';
            if(!ch[u][id]) return 0;
            u = ch[u][id];
        }
        return val[u];
    }
};

7. AC自动机

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

const int maxn = 1e6 + 5;
const int maxm = 26;

struct ACautomaton
{
    int ch[maxn][maxm];//ch[i][c]代表结点i的c孩子;初始有一个根节点,代表空字符串
    int val[maxn];//val为正代表这是一个模式串单词结点
    int fail[maxn];//suffix link,代表当前路径字符串的最大前缀
    int last[maxn];//output link, 上一个单词结点
    int tot;//Trie树中结点总数

    void init()
    {
        tot = 1;
        val[0] = 0;
        memset(ch[0], 0, sizeof ch[0]);
    }

    //O(n),n为所有模式总长度
    void add(char *P, int v)//插入模式串,值为v
    {
        int u = 0;//当前结点
        int n = strlen(P);
        for(int i = 0; i < n; ++i)
        {
            int c = P[i] - 'a';
            if(!ch[u][c])//若当前结点无c孩子,则创造一个
            {
                memset(ch[tot], 0, sizeof ch[tot]);
                val[tot] = 0;//中间结点的值为零
                ch[u][c] = tot++;
            }
            u = ch[u][c];//走向当前结点的c孩子
        }
        //现在走到了模式串的结尾结点
        val[u] += v;
    }

    //O(tot)的
    void getFail()//构造fail指针和last指针
    //使用BFS,因为fail指针一定指向长度更短的字符串
    {
        queue<int> q;
        fail[0] = 0;
        //初始化队列
        for(int c = 0; c < maxm; ++c)
        {
            int u = ch[0][c];
            if(u)
            {
                fail[u] = last[u] = 0;//第一层结点的fail都是根节点
                q.push(u);//将第一层结点加入队列
            }
        }

        //BFS
        while(!q.empty())
        {
            int cur = q.front();
            q.pop();
            for(int c = 0; c < maxm; ++c)//为cur结点的c孩子添加fail指针
            {
                int u = ch[u][c];
                if(!u)//当前结点没有c孩子
                {
                    ch[cur][c] = ch[fail[cur]][c];//沿fail往上找,因为fail指针指向的还是这个后缀
                    continue;
                }
                q.push(u);//c孩子入队
                int v = fail[cur];
                while(v && !ch[v][c]) v = fail[v];//若后缀结点无c孩子,就沿fail指针一直网上找
                fail[u] = ch[v][c];//给c孩子添加fail指针
                //若c孩子的fail指针指向模式串结点,则c孩子的last指向fail指针位置即可,因为这就是最长的
                //否则指向fail指针指向的结点的last即可
                if(val[fail[u]]) last[u] = fail[u];
                else last[u] = last[fail[u]];
            }
        }
    }
};
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值