字符串(kmp匹配 ;马拉车回文串;多串字典树)

目录

1、KMP(自匹配,且只与开头比较)

2、kmp(两个字符串匹配,来找出最长公共字符串)

3、马拉车算法

//给定一个字符串,尽可能的少添加字符使其整体构成一个回文串

//有多少个回文子串(输出以其为开头的最长即可)

4、多个字符串匹配(字典树)

1、给出N 个单词组成的熟词表,以及一篇全用小写英文书写的文章; 将n个单词建立成字典树; 这一大串字符串到字典树中找

2、多个字符串的公共前缀 建字典树;找最近公共祖先 


1、KMP(自匹配,且只与开头比较)

#include<bits/stdc++.h>
using namespace std;
int nextt[15];
void prenext(string s)
{
    int len = s.length();
    int j = 0;
    nextt[0] = 0;
    for(int i = 1; i<len; i++) ///由于第一个字符肯定next为0(因为最大公共前后缀不能为本身),所以从1开始
    {
        while(s[i]!=s[j]&&j!=0) j = nextt[j-1];///j=0跳出循环

        if(s[i]==s[j]) j++;
        nextt[i] = j;
    }
}

2、kmp(两个字符串匹配,来找出最长公共字符串)

#include<stdio.h> 
 
#define N 100010
#define M 100010
char p[N],s[N];
int n,m;//n是子串的长度,m是主串的长度 
int ne[N];//next数组 
int main()
{
	scanf("%d%s%d%s",&n,&p,&m,&s); 
	//求next数组的过程
   ne[0]=0;
	for (int i = 1, j = 0; i < n; i++)//下标从0开始
	{
		while (j&& p[i]!= p[j])  j = ne[j-1];//求字串的next数组,j指向子串的起始位置,i指向子串的起始位置的下一个位置 
		if (p[i] == p[j])  j++;//如果相等,j指向子串的下一个位置 
		ne[i] = j;//子串中下标为i的字符前的字符串最长相等前后缀的长度为j
	}
 int maxx=-1;
	for (int i = 0, j = 0; i <m; i++)//i的话是遍历一遍主串的所有字母,j是从子串每次从零开始遍历,看能不能匹配成功,
	{
		while (j && s[i] != p[j])//j未回到起点,并且长串里的s[i]和短串里的p[j]匹配失败
		{
			j = ne[j-1];//把j移到next[j-1]的位置。
		}
		if (s[i] == p[j ]) j++;
		if (j == n)//j走到子串的头了 
		{
			printf("binggo");
		}
  		else
           {
            	maxx=max(maxx,j);
           }
	}
	return 0;//时间复杂度为O(n)
}

//
#include<bits/stdc++.h>
using namespace std;
#define MAXX 1000000
int tnext[MAXX];
int snext[MAXX];
void kmp(string s,string t)
{
    tnext[0]=0;
    snext[0]=0;
    int maxx=-1;
    int j=0;
    for(int i=1;i<t.length();i++)
    {
        //若一开始不匹配即j==0,会一直0下去
        //所以不匹配要在j!=0下,回前看:
        while(j&&t[i]!=t[j])j=tnext[j-1];
        if(t[i]==t[j])j++;
        tnext[i]=j;
    }
    for(int i=0,j=0;i<s.length();i++)
    {
        while(j&&s[i]!=t[i])j=tnext[j-1];
        if(s[i]==t[j])j++;
        snext[i]=j;
        maxx=max(maxx,j);
        if(maxx==t.length())
        {
            cout<<"binggo";
            break;
        }
    }
    if(maxx!=t.length())
    {
        cout<<maxx<<endl;
    }
}

3、马拉车算法

//给定一个字符串,尽可能的少添加字符使其整体构成一个回文串

//有多少个回文子串(输出以其为开头的最长即可)

#include <iostream>
using namespace std;
string s;
char help[100];
int n;
int search(int index, int step){
    int res = 1 + 2 * step;
    int L = index - 1 - step;
    int R = index + 1 + step;
    while(L >= 0 && R <= 2 * n){
        if(help[L] != help[R])
            break;
        L--, R++;
        res += 2;
    }
    return res/2;
}
int getMax(){
    int max = 0;
    for(int i = 0; i < 2*n+1; i++){
        int temp = search(i, 0);
        //cout << temp << endl;
        max= temp > max ? temp : max;
    }
    return max;
}
int *manacher(){
    int *res = new int[2*n+1];
    int R = -1, L;
    int C, imageCur, imageL;
    int cur = 0, curR;
    while(cur < 2*n+1){
        if(cur > R){
            C = cur;
            curR = search(cur, 0);
            R = R + curR + 1;
            res[cur] = curR;
        }else{
            imageCur = 2 * C - cur;
            L = 2 * C - R;
            imageL = imageCur - res[imageCur];
            if(imageL > L){
                res[cur] = res[imageCur];
            }else if(imageL < L){
                res[cur] = R - cur;
            }else if(imageL == L){
                curR = search(cur, R - cur);
                if(cur + curR > R){
                    C = cur;
                    R = cur + curR;
                }
                res[cur] = curR;
            }
        }
        cur++;
    }
    return res;
}
void input(){
    cin >> s;
    n = s.length();
    for(int i = 1; i < 2*n+1;){
      help[i] = s[(i-1)/2];
      help[i-1] = '#';
      i += 2;
    }
    help[2*n] = '#';
    for(int i = 0; i < 2*n+1;){
      cout << help[i] << " ";
      i ++;
    }
    cout << endl;
}
void printSub(int C, int R){
    for(int i = C - R + 1 ; i < C+ R;){
        cout << help[i];
        i += 2;
    }
    cout << endl;
}
void printAllSub(int res[]){
    for(int i = 1; i < 2*n+1; i++){
        if(res[i] > 1)
            printSub(i, res[i]);
    }
}
//给定一个字符串,尽可能的少添加字符使其整体构成一个回文串
int *getMin(){
    int *res = new int[2*n+1];
    int R = -1, L;
    int C, imageCur, imageL;
    int cur = 0, curR;
    while(cur < 2*n+1){
        if(R == 2*n) break;
        if(cur > R){
            C = cur;
            curR = search(cur, 0);
            R = R + curR + 1;
            res[cur] = curR;
        }else{
            imageCur = 2 * C - cur;
            L = 2 * C - R;
            imageL = imageCur - res[imageCur];
            if(imageL > L){
                res[cur] = res[imageCur];
            }else if(imageL < L){
                res[cur] = R - cur;
            }else if(imageL == L){
                curR = search(cur, R - cur);
                if(cur + curR > R){
                    C = cur;
                    R = cur + curR;
                }
                res[cur] = curR;
            }
        }
        cur++;
    }
    return new int[2] {C, res[C]};
}
void printSuffix(int C, int R){
    int length = 0;
    printSub((2*n+1)/2, (2*n+1)/2);
    for(int i = C - R - 1; i >= 1;){
        cout << help[i];
        i -= 2;
        length++;
    }
    cout << " " << length << endl;
}
//给定一个字符串,尽可能的少添加字符使其整体构成一个回文串
int main()
{
    input();
    cout << "The longest sub-string length is : " << getMax() << endl;
    int *res = manacher();
    for(int i = 0; i < 2*n+1; i++){
        cout << res[i] << " " ;
    }
    cout << endl;
    printAllSub(res);
    return 0;
}

输出所有回文子串(向中心扩展)

void expand(string str, int low, int high, auto &set)
{
    // 运行直到 `str[low.high]` 不是回文
    while (low >= 0 && high < str.length() && str[low] == str[high])
    {
        // 将是回文推入一个集合
        set.insert(str.substr(low, high - low + 1));
        // 从中心向两边展开
        low--,high++;
    }
}
// 查找给定字符串的所有唯一回文子串的函数
void findPalindromicSubstrings(string str)
{
    // 存所有唯一的回文子串
    unordered_set<string> set;
    for (int i = 0; i < str.length(); i++)
    {
        // 所有以 `str[i]` 为中点的奇数回文数
        expand(str, i, i, set);
        // 所有偶数长度的回文 // 它的中点
        expand(str, i, i + 1, set);
    }

    // 打印所有唯一的回文子串
 for (auto i: set) { 
 cout << i << " "; 
}

4、多个字符串匹配(字典树)

1、给出N 个单词组成的熟词表,以及一篇全用小写英文书写的文章; 将n个单词建立成字典树; 这一大串字符串到字典树中找

2、多个字符串的公共前缀 建字典树;找最近公共祖先 

 void insert(const string& s,int index_of_s){
        int r = 0;
        for(int i=0;i<s.size();++i){
            int idx = s[i] - 'a';
            
            int& next = childs[r][idx];
            //printf("%d %d %d %d\n",r,idx,next,nodes_cnt);
            if(next == 0){
                next = ++nodes_cnt;
            }
            r = next;
        }
        values[r] = index_of_s;
    }
 void find(const string & t,int beg,vector<vector<int>>& ans){
        int r = 0;
        for(int j = beg;j < t.size();++j){
            int idx = t[j] - 'a';
            const int& next = childs[r][idx];
            if(next == 0){//没找到
                return ;
            }
            if(values[next] >= 0){//t[beg,j]是一个串,记录开始位置beg
                ans[values[next]].push_back(beg);
            }
            r = next;
        }
    }
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值