1. 程式人生 > >ACM演算法模板 · 一些常用的演算法模板-模板合集(打比賽專用)

ACM演算法模板 · 一些常用的演算法模板-模板合集(打比賽專用)

0.標頭檔案

#define _CRT_SBCURE_NO_DEPRECATE
#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional> using namespace std; const int maxn = 110; const int INF = 0x3f3f3f3f;

經典

1.埃拉託斯特尼篩法

/*
    |埃式篩法|
    |快速篩選素數|
    |16/11/05ztx|
*/

int prime[maxn];  
bool is_prime[maxn];

int sieve(int n){
    int p = 0;
    for(int i = 0; i <= n; ++i)
        is_prime[i] = true;
    is_prime[0
] = is_prime[1] = false; for (int i = 2; i <= n; ++i){ // 注意陣列大小是n if(is_prime[i]){ prime[p++] = i; for(int j = i + i; j <= n; j += i) // 輕剪枝,j必定是i的倍數 is_prime[j] = false; } } return p; // 返回素數個數 }

2.快速冪

/*
    |快速冪|
    |16/11/05ztx|
*/
typedef long long LL; // 視資料大小的情況而定 LL powerMod(LL x, LL n, LL m) { LL res = 1; while (n > 0){ if (n & 1) // 判斷是否為奇數,若是則true res = (res * x) % m; x = (x * x) % m; n >>= 1; // 相當於n /= 2; } return res; }

3.大數模擬

大數加法

/*
    |大數模擬加法|
    |用string模擬|
    |16/11/05ztx, thanks to caojiji|
*/

string add1(string s1, string s2)
{
    if (s1 == "" && s2 == "")   return "0";
    if (s1 == "")   return s2;
    if (s2 == "")   return s1;
    string maxx = s1, minn = s2;
    if (s1.length() < s2.length()){
        maxx = s2;
        minn = s1;
    }
    int a = maxx.length() - 1, b = minn.length() - 1;
    for (int i = b; i >= 0; --i){
        maxx[a--] += minn[i] - '0'; //  a一直在減 , 額外還要減個'0'
    }
    for (int i = maxx.length()-1; i > 0;--i){
        if (maxx[i] > '9'){
            maxx[i] -= 10;//注意這個是減10
            maxx[i - 1]++;
        }
    }
    if (maxx[0] > '9'){
        maxx[0] -= 10;
        maxx = '1' + maxx;
    }
    return maxx;
}

大數階乘

/*
    |大數模擬階乘|
    |用陣列模擬|
    |16/12/02ztx|
*/

#include <iostream>
#include <cstdio>

using namespace std;

typedef long long LL;

const int maxn = 100010;

int num[maxn], len;

/*
    在mult函式中,形參部分:len每次呼叫函式都會發生改變,n表示每次要乘以的數,最終返回的是結果的長度
    tip: 階乘都是先求之前的(n-1)!來求n!
    初始化Init函式很重要,不要落下
*/

void Init() {
    len = 1;
    num[0] = 1;
}

int mult(int num[], int len, int n) {
    LL tmp = 0;
    for(LL i = 0; i < len; ++i) {
         tmp = tmp + num[i] * n;    //從最低位開始,等號左邊的tmp表示當前位,右邊的tmp表示進位(之前進的位)
         num[i] = tmp % 10; //  儲存在對應的陣列位置,即去掉進位後的一位數
         tmp = tmp / 10;    //  取整用於再次迴圈,與n和下一個位置的乘積相加
    }
    while(tmp) {    //  之後的進位處理
         num[len++] = tmp % 10;
         tmp = tmp / 10;
    }
    return len;
}

int main() {
    Init();
    int n;
    n = 1977; // 求的階乘數
    for(int i = 2; i <= n; ++i) {
        len = mult(num, len, i);
    }
    for(int i = len - 1; i >= 0; --i)
        printf("%d",num[i]);    //  從最高位依次輸出,資料比較多采用printf輸出
    printf("\n");
    return 0;
}

4.GCD

/*
    |輾轉相除法|
    |歐幾里得演算法|
    |求最大公約數|
    |16/11/05ztx|
*/

int gcd(int big, int small)
{
    if (small > big) swap(big, small);
    int temp;
    while (small != 0){ //  輾轉相除法
        if (small > big) swap(big, small);
        temp = big % small;
        big = small;
        small = temp;
    }
    return(big);
}

5.LCM

/*
    |輾轉相除法|
    |歐幾里得演算法|
    |求最小公倍數|
    |16/11/05ztx|
*/

int gcd(int big, int small)
{
    if (small > big) swap(big, small);
    int temp;
    while (small != 0){ //  輾轉相除法
        if (small > big) swap(big, small);
        temp = big % small;
        big = small;
        small = temp;
    }
    return(big);
}

6.全排列

/*
    |求1到n的全排列, 有條件|
    |16/11/05ztx, thanks to wangqiqi|
*/

void Pern(int list[], int k, int n) {   //  k表示前k個數不動僅移動後面n-k位數
    if (k == n - 1) {
        for (int i = 0; i < n; i++) {
            printf("%d", list[i]);
        }
        printf("\n");
    }else {
        for (int i = k; i < n; i++) {   //  輸出的是滿足移動條件所有全排列
            swap(list[k], list[i]);
            Pern(list, k + 1, n);
            swap(list[k], list[i]);
        }
    }
}

7.二分搜尋

/*
    |二分搜尋|
    |要求:先排序|
    |16/11/05ztx, thanks to wangxiaocai|
*/

//  left為最開始元素, right是末尾元素的下一個數,x是要找的數
int bsearch(int *A, int left, int right, int x){
    int m;
    while (left < right){
        m = left + (right - left) / 2;
        if (A[m] >= x)  right = m;   else left = m + 1;    
        // 如果要替換為 upper_bound, 改為:if (A[m] <= v) x = m+1; else y = m;     
    }
    return left;
}

/*
    最後left == right  

    如果沒有找到1355776,返回7  

    如果找有多少的x,可以用lower_bound查詢一遍,upper_bound查詢一遍,下標相減  

    C++自帶的lower_bound(a,a+n,x)返回陣列中最後一個x的下一個數的地址 

    upper_bound(a,a+n,x)返回陣列中第一個x的地址

    如果a+n內沒有找到x或x的下一個地址,返回a+n的地址  

    lower_bound(a,a+n,x)-upper_bound(a,a+n,x)返回陣列中x的個數
*/ 

資料結構

並查集

8.並查集

/*
    |合併節點操作|
    |16/11/05ztx, thanks to chaixiaojun|
*/

int father[maxn];   //  儲存i的father父節點  

void makeSet() {  
    for (int i = 0; i < maxn; i++)   
        father[i] = i;  
}  

int findRoot(int x) {   //  迭代找根節點
    int root = x; // 根節點  
    while (root != father[root]) { // 尋找根節點  
        root = father[root];  
    }  
    while (x != root) {  
        int tmp = father[x];  
        father[x] = root; // 根節點賦值  
        x = tmp;  
    }  
    return root;  
}  

void Union(int x, int y) {  //  將x所在的集合和y所在的集合整合起來形成一個集合。  
    int a, b;  
    a = findRoot(x);  
    b = findRoot(y);  
    father[a] = b;  // y連在x的根節點上   或father[b] = a為x連在y的根節點上;  
}  

/*
    在findRoot(x)中:
    路徑壓縮 迭代 最優版
    關鍵在於在路徑上的每個節點都可以直接連線到根上
*/

圖論

MST

最小生成樹

Kruskal

9.克魯斯卡爾演算法

/*
    |Kruskal演算法|
    |適用於 稀疏圖 求最小生成樹|
    |16/11/05ztx thanks to wangqiqi|
*/

/*
    第一步:點、邊、加入vector,把所有邊按從小到大排序
    第二步:並查集部分 + 下面的code
*/

void Kruskal() {    
    ans = 0;    
    for (int i = 0; i<len; i++) {    
        if (Find(edge[i].a) != Find(edge[i].b)) {    
            Union(edge[i].a, edge[i].b);    
            ans += edge[i].len;    
        }    
    }    
}    

Prim

10.普里姆演算法

/*
    |Prim演算法|
    |適用於 稠密圖 求最小生成樹|
    |堆優化版,時間複雜度:O(elgn)|
    |16/11/05ztx, thanks to chaixiaojun|
*/

struct node {  
    int v, len;  
    node(int v = 0, int len = 0) :v(v), len(len) {}  
    bool operator < (const node &a)const {  // 加入佇列的元素自動按距離從小到大排序  
        return len> a.len;  
    }  
};

vector<node> G[maxn];
int vis[maxn];
int dis[maxn];

void init() {  
    for (int i = 0; i<maxn; i++) {  
        G[i].clear();  
        dis[i] = INF;  
        vis[i] = false;  
    }  
}  
int Prim(int s) {  
    priority_queue<node>Q; // 定義優先佇列  
    int ans = 0;  
    Q.push(node(s,0));  // 起點加入佇列  
    while (!Q.empty()) {   
        node now = Q.top(); Q.pop();  // 取出距離最小的點  
        int v = now.v;  
        if (vis[v]) continue;  // 同一個節點,可能會推入2次或2次以上佇列,這樣第一個被標記後,剩下的需要直接跳過。  
        vis[v] = true;  // 標記一下  
        ans += now.len;  
        for (int i = 0; i<G[v].size(); i++) {  // 開始更新  
            int v2 = G[v][i].v;  
            int len = G[v][i].len;  
            if (!vis[v2] && dis[v2] > len) {   
                dis[v2] = len;  
                Q.push(node(v2, dis[v2]));  // 更新的點加入佇列並排序  
            }  
        }  
    }  
    return ans; 
}  

Bellman-Ford

單源最短路

Dijkstra

11.迪傑斯特拉演算法

/*
    |Dijkstra演算法|
    |適用於邊權為正的有向圖或者無向圖|
    |求從單個源點出發,到所有節點的最短路|
    |優化版:時間複雜度 O(elbn)|
    |16/11/05ztx, thanks to chaixiaojun|
*/

struct node {  
    int v, len;  
    node(int v = 0, int len = 0) :v(v), len(len) {}  
    bool operator < (const node &a)const {  //  距離從小到大排序  
        return len > a.len;  
    }  
};  

vector<node>G[maxn];  
bool vis[maxn];  
int dis[maxn];

void init() {  
    for (int i = 0; i<maxn; i++) {  
        G[i].clear();  
        vis[i] = false;  
        dis[i] = INF;  
    }  
}  
int dijkstra(int s, int e) {  
    priority_queue<node>Q;  
    Q.push(node(s, 0)); //  加入佇列並排序  
    dis[s] = 0;  
    while (!Q.empty()) {  
        node now = Q.top();     //  取出當前最小的  
        Q.pop();  
        int v = now.v;  
        if (vis[v]) continue;   //  如果標記過了, 直接continue  
        vis[v] = true;  
        for (int i = 0; i<G[v].size(); i++) {   //  更新  
            int v2 = G[v][i].v;  
            int len = G[v][i].len;  
            if (!vis[v2] && dis[v2] > dis[v] + len) {  
                dis[v2] = dis[v] + len;  
                Q.push(node(v2, dis[v2]));  
            }  
        }  
    }  
    return dis[e];  
}  

SPFA

12.最短路徑快速演算法(Shortest Path Faster Algorithm)

/*
    |SPFA演算法|
    |佇列優化|
    |可處理負環|
*/

vector<node> G[maxn];
bool inqueue[maxn];
int dist[maxn];

void Init()  
{  
    for(int i = 0 ; i < maxn ; ++i){  
        G[i].clear();  
        dist[i] = INF;  
    }  
}  
int SPFA(int s,int e)  
{  
    int v1,v2,weight;  
    queue<int> Q;  
    memset(inqueue,false,sizeof(inqueue)); // 標記是否在佇列中  
    memset(cnt,0,sizeof(cnt)); // 加入佇列的次數  
    dist[s] = 0;  
    Q.push(s); // 起點加入佇列  
    inqueue[s] = true; // 標記  
    while(!Q.empty()){  
        v1 = Q.front();  
        Q.pop();  
        inqueue[v1] = false; // 取消標記  
        for(int i = 0 ; i < G[v1].size() ; ++i){ // 搜尋v1的連結串列  
            v2 = G[v1][i].vex;  
            weight = G[v1][i].weight;  
            if(dist[v2] > dist[v1] + weight){ // 鬆弛操作  
                dist[v2] = dist[v1] + weight;  
                if(inqueue[v2] == false){  // 再次加入佇列  
                    inqueue[v2] = true;  
                    //cnt[v2]++;  // 判負環  
                    //if(cnt[v2] > n) return -1;  
                    Q.push(v2);  
                } } }  
    }  
    return dist[e];  
}

/*
    不斷的將s的鄰接點加入佇列,取出不斷的進行鬆弛操作,直到佇列為空  

    如果一個結點被加入佇列超過n-1次,那麼顯然圖中有負環  
*/

Floyd-Warshall

13.弗洛伊德演算法

/*
    |Floyd演算法|
    |任意點對最短路演算法|
    |求圖中任意兩點的最短距離的演算法|
*/

for (int i = 0; i < n; i++) {   //  初始化為0  
    for (int j = 0; j < n; j++)  
        scanf("%lf", &dis[i][j]);  
}  
for (int k = 0; k < n; k++) {  
    for (int i = 0; i < n; i++) {  
        for (int j = 0; j < n; j++) {  
            dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);  
        }  
    }
}

二分圖

14.染色法

/*
    |交叉染色法判斷二分圖|
    |16/11/05ztx|
*/

int bipartite(int s) {  
    int u, v;  
    queue<int>Q;  
    color[s] = 1;  
    Q.push(s);  
    while (!Q.empty()) {  
        u = Q.front();  
        Q.pop();  
        for (int i = 0; i < G[u].size(); i++) {  
            v = G[u][i];  
            if (color[v] == 0) {  
                color[v] = -color[u];  
                Q.push(v);  
            }  
            else if (color[v] == color[u])  
                return 0;  
        }  
    }  
    return 1;  
}  

15..匈牙利演算法

/*
    |求解最大匹配問題|
    |遞迴實現|
    |16/11/05ztx|
*/

vector<int>G[maxn];  
bool inpath[maxn];  //  標記  
int match[maxn];    //  記錄匹配物件  
void init()  
{  
    memset(match, -1, sizeof(match));  
    for (int i = 0; i < maxn; ++i) {  
        G[i].clear();  
    }  
}  
bool findpath(int k) {  
    for (int i = 0; i < G[k].size(); ++i) {  
        int v = G[k][i];  
        if (!inpath[v]) {  
            inpath[v] = true;  
            if (match[v] == -1 || findpath(match[v])) { // 遞迴  
                match[v] = k; // 即匹配物件是“k妹子”的  
                return true;  
            }  
        }  
    }  
    return false;  
}  

void hungary() {  
    int cnt = 0;  
    for (int i = 1; i <= m; i++) {  // m為需要匹配的“妹子”數  
        memset(inpath, false, sizeof(inpath)); // 每次都要初始化  
        if (findpath(i)) cnt++;  
    }  
    cout << cnt << endl;  
}  
/*
    |求解最大匹配問題|
    |dfs實現|
    |16/11/05ztx|
*/

int v1, v2;  
bool Map[501][501];  
bool visit[501];  
int link[501];  
int result;  

bool dfs(int x)  {  
    for (int y = 1; y <= v2; ++y)  {  
        if (Map[x][y] && !visit[y])  {  
            visit[y] = true;  
            if (link[y] == 0 || dfs(link[y]))  {  
                link[y] = x;  
                return true;  
            } } }  
    return false;  
}  


void Search()  {  
    for (int x = 1; x <= v1; x++)  {  
        memset(visit,false,sizeof(visit));  
        if (dfs(x))  
            result++;  
    }
}

動態規劃

揹包

16.17.18揹包問題

/*
    |01揹包|
    |完全揹包|
    |多重揹包|
    |16/11/05ztx|
*/

//  01揹包:  

void bag01(int cost,int weight)  {  
    for(i = v; i >= cost; --i)  
    dp[i] = max(dp[i], dp[i-cost]+weight);  
}  

//  完全揹包:  

void complete(int cost, int weight)  {  
    for(i = cost ; i <= v; ++i)  
    dp[i] = max(dp[i], dp[i - cost] + weight);  
}  

//  多重揹包:  

void multiply(int cost, int weight, int amount)  {  
    if(cost * amount >= v)  
        complete(cost, weight);  
    else{  
        k = 1;  
        while (k < amount){  
            bag01(k * cost, k * weight);  
            amount -= k;  
            k += k;  
        }  
        bag01(cost * amount, weight * amount);  
    }  
}  


// other

int dp[1000000];
int c[55], m[110];
int sum;

void CompletePack(int c) {
    for (int v = c; v <= sum / 2; ++v){
        dp[v] = max(dp[v], dp[v - c] + c);
    }
}

void ZeroOnePack(int c) {
    for (int v = sum / 2; v >= c; --v) {
        dp[v] = max(dp[v], dp[v - c] + c);
    }
}

void multiplePack(int c, int m) {
    if (m * c > sum / 2)
        CompletePack(c);
    else{
        int k = 1;
        while (k < m){
            ZeroOnePack(k * c);
            m -= k;
            k <<= 1;
        }
        if (m != 0){
            ZeroOnePack(m * c);
        }
    }
}

LIS

19.最長上升子序列

/*
    |最長上升子序列|
    |狀態轉移|
    |16/11/05ztx|
*/

/*
    狀態轉移dp[i] = max{ 1.dp[j] + 1 };  j<i; a[j]<a[i];
    d[i]是以i結尾的最長上升子序列
    與i之前的 每個a[j]<a[i]的 j的位置的最長上升子序列+1後的值比較
*/

void solve(){   // 參考挑戰程式設計入門經典;
    for(int i = 0; i < n; ++i){  
        dp[i] = 1;  
        for(int j = 0; j < i; ++j){  
            if(a[j] < a[i]){  
                dp[i] = max(dp[i], dp[j] + 1);  
            } } }
}  

/* 
    優化方法:
    dp[i]表示長度為i+1的上升子序列的最末尾元素  
    找到第一個比dp末尾大的來代替 
*/

    void solve() {  
        for (int i = 0; i < n; ++i){
            dp[i] = INF;
        }
        for (int i = 0; i < n; ++i) {  
            *lower_bound(dp, dp + n, a[i]) = a[i];  //  返回一個指標  
        }  
        printf("%d\n", *lower_bound(dp, dp + n, INF) - dp;  
    }

/*  
    函式lower_bound()返回一個 iterator 它指向在[first,last)標記的有序序列中可以插入value,而不會破壞容器順序的第一個位置,而這個位置標記了一個不小於value的值。
*/

LCS

20.最長公共子序列

/*
    |求最長公共子序列|
    |遞推形式|
    |16/11/05ztx|
*/

void solve() {  
    for (int i = 0; i < n; ++i) {  
        for (int j = 0; j < m; ++j) {  
            if (s1[i] == s2[j]) {  
                dp[i + 1][j + 1] = dp[i][j] + 1;  
            }else {  
                dp[i + 1][j + 1] = max(dp[i][j + 1], dp[i + 1][j]);  
            } } }
}  

計算幾何

21.向量基本用法

/*
    |16/11/06ztx|
*/

struct node {  
    double x; // 橫座標  
    double y; // 縱座標  
};  

typedef node Vector;

Vector operator + (Vector A, Vector B) { return Vector(A.x + B.x, A.y + B.y); }  
Vector operator - (Point A, Point B) { return Vector(A.x - B.y, A.y - B.y); }  
Vector operator * (Vector A, double p) { return Vector(A.x*p, A.y*p); }  
Vector operator / (Vector A, double p) { return Vector(A.x / p, A.y*p); }  

double Dot(Vector A, Vector B) { return A.x*B.x + A.y*B.y; } // 向量點乘  
double Length(Vector A) { return sqrt(Dot(A, A)); }  // 向量模長  
double Angle(Vector A, Vector B) { return acos(Dot(A, B) / Length(A) / Length(B)); }  // 向量之間夾角  

double Cross(Vector A, Vector B) { // 叉積計算 公式  
    return A.x*B.y - A.y*B.x;  
}  

Vector Rotate(Vector A, double rad) // 向量旋轉 公式  {  
    return Vector(A.x*cos(rad) - A.y*sin(rad), A.x*sin(rad) + A.y*cos(rad));  
}  

Point getLineIntersection(Point P, Vector v, Point Q, Vector w) { // 兩直線交點t1 t2計算公式   
    Vector u = P - Q;   
    double t = Cross(w, u) / Cross(v, w);  // 求得是橫座標  
    return P + v*t;  // 返回一個點  
}  

22.求多邊形面積

/*
    |16/11/06ztx|
*/

node G[maxn];  
int n;  

double Cross(node a, node b) { // 叉積計算  
    return a.x*b.y - a.y*b.x;  
}  


int main()  
{  
    while (scanf("%d", &n) != EOF && n) {  
        for (int i = 0; i < n; i++)   
            scanf("%lf %lf", &G[i].x, &G[i].y);  
        double sum = 0;  
        G[n].x = G[0].x;  
        G[n].y = G[0].y;  
        for (int i = 0; i < n; i++) {   
                sum += Cross(G[i], G[i + 1]);  
        }  
        // 或者  
            //for (int i = 0; i < n; i++) {  
                //sum += fun(G[i], G[(i + 1)% n]);  
            //}  
        sum = sum / 2.0;  
        printf("%.1f\n", sum);  
    }  
    system("pause");  
    return 0;  
}

23..判斷線段相交

/*
    |16/11/06ztx|
*/

node P[35][105];     

double Cross_Prouct(node A,node B,node C) {     //  計算BA叉乘CA     
    return (B.x-A.x)*(C.y-A.y)-(B.y-A.y)*(C.x-A.x);      
}      
bool Intersect(node A,node B,node C,node D)  {  //  通過叉乘判斷線段是否相交;           
    if(min(A.x,B.x)<=max(C.x,D.x)&&         //  快速排斥實驗;      
       min(C.x,D.x)<=max(A.x,B.x)&&      
       min(A.y,B.y)<=max(C.y,D.y)&&      
       min(C.y,D.y)<=max(A.y,B.y)&&      
       Cross_Prouct(A,B,C)*Cross_Prouct(A,B,D)<0&&      //  跨立實驗;      
       Cross_Prouct(C,D,A)*Cross_Prouct(C,D,B)<0)       //  叉乘異號表示在兩側;      
       return true;      
    else return false;      
}    

24.求三角形外心

/*
    |16/11/06ztx|
*/

Point circumcenter(const Point &a, const Point &b, const Point &c) { //返回三角形的外心        
    Point ret;  
    double a1 = b.x - a.x, b1 = b.y - a.y, c1 = (a1*a1 + b1*b1) / 2;  
    double a2 = c.x - a.x, b2 = c.y - a.y, c2 = (a2*a2 + b2*b2) / 2;  
    double d = a1*b2 - a2*b1;  
    ret.x = a.x + (c1*b2 - c2*b1) / d;  
    ret.y = a.y + (a1*c2 - a2*c1) / d;  
    return ret;  
}  

24.極角排序

/*
    |16/11/06ztx|
*/

double cross(point p1, point p2, point q1, point q2) {  // 叉積計算   
    return (q2.y - q1.y)*(p2.x - p1.x) - (q2.x - q1.x)*(p2.y - p1.y);  
}  
bool cmp(point a, point b)  {  
    point o;  
    o.x = o.y = 0;  
    return cross(o, b, o, a) < 0; // 叉積判斷  
}  
sort(convex + 1, convex + cnt, cmp); // 按角排序, 從小到大 

字串

kmp

25.克努特-莫里斯-普拉特操作

/*
    |kmp演算法|
    |字串匹配|
    |17/1/21ztx|
*/

void getnext(char str[maxn], int nextt[maxn]) {
    int j = 0, k = -1;
    nextt[0] = -1;
    while (j < m) {
        if (k == -1 || str[j] == str[k]) {
            j++;
            k++;
            nextt[j] = k;
        }
        else
            k = nextt[k];
    }
}

void kmp(int a[maxn], int b[maxn]) {    
    int nextt[maxm];    
    int i = 0, j = 0;    
    getnext(b, nextt);    
    while (i < n) {    
        if (j == -1 || a[i] == b[j]) { // 母串不動,子串移動    
            j++;    
            i++;    
        }    
        else {    
            // i不需要回溯了    
            // i = i - j + 1;    
            j = nextt[j];    
        }    
        if (j == m) {    
            printf("%d\n", i - m + 1); // 母串的位置減去子串的長度+1    
            return;    
        }    
    }    
    printf("-1\n");
}    

26.kmp擴充套件

/*
    |16/11/06ztx|
*/

#include<iostream>    
#include<cstring>    

using namespace std;

const int MM=100005;    

int next[MM],extand[MM];    
char S[MM],T[MM];    

void GetNext(const char *T) {    
    int len = strlen(T),a = 0;    
    next[0] = len;    
    while(a < len - 1 && T[a] == T[a + 1]) a++;    
    next[1] = a;    
    a = 1;    
    for(int k = 2; k < len; k ++) {    
        int p = a + next[a] - 1,L = next[k - a];    
        if( (k - 1) + L >= p) {    
            int j = (p - k + 1) > 0 ? (p - k + 1) : 0;    
            while(k + j < len && T[k + j] == T[j]) j++;    
            next[k] = j;    
            a = k;    
        }else next[k] = L;    
    }    
}    
void GetExtand(const char *S,const char *T) {    
    GetNext(T);    
    int slen = strlen(S),tlen = strlen(T),a = 0;    
    int MinLen = slen < tlen ? slen : tlen;    
    while(a < MinLen && S[a] == T[a]) a++;    
    extand[0] = a;     
    a = 0;    
    for(int k = 1; k < slen; k ++) {    
        int p = a + extand[a] - 1, L = next[k - a];    
        if( (k - 1) + L >= p) {    
            int j = (p - k + 1) > 0 ? (p - k + 1) : 0;    
            while(k + j < slen && j < tlen && S[k + j] == T[j]) j ++;    
            extand[k] = j;    
            a = k;    
        } else    
            extand[k] = L;    
    }    
}    
void show(const int *s,int len){    
    for(int i = 0; i < len; i ++)    
            cout << s[i] << ' ';    
    cout << endl;    
}    

int main() {    
    while(cin >> S >> T) {    
        GetExtand(S,T);    
        show(next,strlen(T));    
        show(extand,strlen(S));    
    }    
    return 0;    
}   

字典樹

27.字典樹

/*
    |16/11/06ztx|
*/

struct Trie{  
    int cnt;  
    Trie *next[maxn];  
    Trie(){  
        cnt = 0;  
        memset(next,0,sizeof(next));  
    }  
};  

Trie *root;  

void Insert(char *word)  {  
    Trie *tem = root;  
    while(*word != '\0')  {  
        int x = *word - 'a';  
        if(tem->next[x] == NULL)  
            tem->next[x] = new Trie;  
        tem = tem->next[x];  
        tem->cnt++;  
        word++;  
    }  
}  

int Search(char *word)  {  
    Trie *tem = root;  
    for(int i=0;word[i]!='\0';i++)  {  
        int x = word[i]-'a';  
        if(tem->next[x] == NULL)  
            return 0;  
        tem = tem->next[x];  
    }  
    return tem->cnt;  
}  

void Delete(char *word,int t) {  
    Trie *tem = root;  
    for(int i=0;word[i]!='\0';i++)  {  
        int x = word[i]-'a';  
        tem = tem->next[x];  
        (tem->cnt)-=t;  
    }  
    for(int i=0;i<maxn;i++)  
        tem->next[i] = NULL;  
}  

int main() {  
    int n;  
    char str1[50];  
    char str2[50];  
    while(scanf("%d",&n)!=EOF)  {  
        root = new Trie;  
        while(n--)  {  
            scanf("%s %s",str1,str2);  
            if(str1[0]=='i') {
                Insert(str2); 
            }else if(str1[0] == 's')  {  
                if(Search(str2))  
                    printf("Yes\n");  
                else  
                    printf("No\n");  
            }else  {  
                int t = Search(str2);  
                if(t)  
                    Delete(str2,t);  
            } } }  
    return 0;  
}  

28.AC自動機

/*
    |16/11/06ztx|
*/

#include<iostream>  
#include<cstdio>  
#include<cstring>  
#include<string>  

using namespace std;  

#define N 1000010  

char str[N], keyword[N];  
int head, tail;  

struct node {  
    node *fail;  
    node *next[26];  
    int count;  
    node() { //init  
        fail = NULL;// 預設為空  
        count = 0;  
        for(int i = 0; i < 26; ++i)  
            next[i] = NULL;  
    }  
}*q[N];  

node *root;  

void insert(char *str)  { // 建立Trie  
    int temp, len;  
    node *p = root;  
    len = strlen(str);  
    for(int i = 0; i < len; ++i)  {  
        temp = str[i] - 'a';  
        if(p->next[temp] == NULL)  
            p->next[temp] = new node();  
        p = p->next[temp];  
    }  
    p->count++;  
}  

void build_ac() { // 初始化fail指標,BFS 陣列模擬佇列:   
    q[tail++] = root;  
    while(head != tail)  {  
        node *p = q[head++]; // 彈出隊頭  
        node *temp = NULL;  
        for(int i = 0; i < 26; ++i)  {  
            if(p->next[i] != NULL)  {  
                if(p == root) { // 第一個元素fail必指向根  
                    p->next[i]->fail = root;
                }else {  
                    temp = p->fail; // 失敗指標  
                    while(temp != NULL) { // 2種情況結束:匹配為空or找到匹配 
                        if(temp->next[i] != NULL) { // 找到匹配  
                            p->next[i]->fail = temp->next[i];  
                            break;