赞
踩
字符串匹配问题是计算机科学中研究最为广泛的问题之一,字符串匹配算法发展了十几年,广泛应用于生物信息学、信息检索等领域。我们先来看一个比较常见的字符串匹配问题。
相关概念:
字符串匹配原则:不重不漏
朴素的模式匹配算法:
S
的第一个字符和模式串T
的第一个字符进行比较;S
的第二个字符和模式串T
第一个字符进行比较S
中匹配到模式串T
或者枚举到母串末尾。BF算法嵌套了两重循环,所以时间复杂度为O(n*m)
。BF算法浅显易懂,易于实现。
算法优化思路:可以用之前的匹配信息避免后继一些无用的匹配动作
int brute_force(const char *s, const char *t) {
for(int i = 0; s[i]; i++) {
int flag = 1;
for(int j = 0; t[j]; j++) {
if(s[i + j] == t[j]) continue;
flag = 0;
break;
}
if(flag) return i;
}
return -1;
}
算法思路:状态机:一个状态根据一个输入跳到另一个状态,KMP非常适合在信息流中匹配字符。
实现步骤:
int kmp(const char *s, const char *t) {
int next[100] = {-1};
for(int i = 1, j = -1; t[i]; i++) {
while(j != -1 && t[i] != t[j + 1]) j = next[j];
if(t[i] == t[j + 1]) j++;
next[i] = j;
}
for(int i = 0, j = -1; s[i]; i++) {
while(j != -1 && s[i] != t[j + 1]) j = next[j];
if(s[i] == t[j + 1]) j++;
if(t[j + 1] == 0) return i - j;
}
return -1;
}
SUNDAY 算法理解的核心,在于理解黄金对齐点位,是文本串的匹配尾部,一定会出现在模式串中的字符应该和模式串中最后一位出现该字符的位置对齐,本质就是利用后缀信息进行加锁。最优时间复杂度为n/m
,应用场景单词查找(ctrl + F)
实现步骤:
int sunday(const char *s, const char *t) {
int ind[256], len_t = strlen(t), len_s = strlen(s);
for(int i = 0; i < 256; i++) ind[i] = len_t + 1;
for(int i = 0; t[i]; i++) ind[t[i]] = len_t - i;
for(int i = 0; i + len_t <= len_s; i+= ind[s[len_t + i]]) {
int flag = 1;
for(int j = 0; t[j]; j++) {
if(s[i + j] == t[j]) continue;
flag = 0;
break;
}
if(flag) return i;
}
return -1;
}
shift-and
算法思路:模式串的预处理->字符编码表(二维数组)。相应字符在哪个位置上,哪个位置上的二进制数字就会变为1。p就是一个数字,代表当前的匹配状态 ,以文本串当前位置作为结尾,向前可以匹配成功模式串的前几位,可以匹配成功几位对应的位置上就是1。最后通过P判断是否匹配成功。
实现步骤:
p_i = (p_{i-1}<<1 | 1) \& d[s_i]
p_i
第 j 位二进制为1,代表当前位置为结尾,可以匹配成功模式串的第 j 位int shift_and(const char *s, const char *t) {
int code[256] = {0}, n = 0, p = 0;
for(int i = 0; t[i]; i++, n++) code[t[i]] |= (1 << i);
for(int i = 0; s[i]; i++) {
p = (p << 1 | 1) & code[s[i]];
if(p & (1 << (n - 1))) return i - n + 1;
}
return -1;
}
Manacher算法主要用于字符串中最长回文字符的查找。时间复杂度接近线性。
实现步骤:
#
字符,得到结果串/*************************************************************************
> File Name: 005.最长回文子串.c
> Author: 陈杰
> Mail: 15193162746@163.com
> Created Time: 2021年05月21日 星期五 16时52分50秒
> 题目描述
给你一个字符串`s`,找到`s`中最长的回文子串
> 马拉车算法
************************************************************************/
#include<stdio.h>
#include<stdlib.h>
char *longestPalindrome(char * s){
char ns[2002] = {0};
int dist[2005]; // 第i为的回文串长度
ns[0] = '#';
for(int i = 0; s[i]; i++) {
ns[2*i + 1] = s[i];
ns[2*i + 2] = '#';
}
// 马拉车算法核心
int l = 0, r = -1;
for(int i = 0; ns[i]; i++) {
// dist[i] 赋初始值
if(i > r) dist[i] = 1;
else dist[i] = dist[l + r - i] > (r - i) ? (r - i) : dist[l + r - i];
// 朴素的回文串判断办法
while(i - dist[i] >= 0 && ns[i - dist[i]] == ns[i + dist[i]]) dist[i]++;
// 更新l r
if(i + dist[i] > r && i - dist[i] > 0) {
l = i - dist[i];
r = i + dist[i];
}
}
// 马拉车算法
char *res = (char *)malloc(sizeof(s));
int tmp = 0, ind = 0;
for(int i = 0; ns[i]; i++) {
if(tmp >= dist[i]) continue;
tmp = dist[i];
ind = 0;
for(int j = i -dist[i] + 1; j < i + dist[i]; j++) {
if(ns[j] != '#') res[ind] = ns[j], ind++;
}
}
res[ind] = 0;
return res;
}
int main() {
char s[1001];
scanf("%s", s);
char *res = longestPalindrome(s);
printf("%s\n", res);
free(res);
return 0;
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。