复工复产第一天
KMP算法分两步,一步是模式串(需要用于匹配的字符串)自己匹配时的处理,另一步就是模式串匹配目标串了。
模式串自己匹配自己时可以理解为对不同长度的自己找有最大公共长度的头尾(但是不能全部长度去匹配),来造出模式串的前缀数组
比如下面字符串
abcdabcdxabcdabcd
长度为7时,有
abcdabc
最大公共长度的头尾为abc
则当前数组为
0 0 0 0 1 2 3
长度为15时
abcdabcdxabcdab
最大公共长度的头尾为abcdab
则当前数组为
0 0 0 0 1 2 3 4 0 1 2 3 4 5 6
如果把模式串拉更长也许能有更好的表现,例如下面(数据点5的模式串),但我懒(
CCBABBACBABCAACBBCAACCBACACCCACCABABBABBABCABCCABABBCCABABACBCCACBACACBABCCBAAAABBBCBBABCBABABBCABCB
它的前缀数组是
0 1 0 0 0 0 0 1 0 0 0 1 0 0 1 0 0 1 0 0 1 2 3 4 1 0 1 2 2 0 1 2 0 0 0 0 0 0 0 0 0 0 1 0 0 1 2 0 0 0 0 0 1 2 0 0 0 0 0 1 0 1 2 0 1 0 0 1 0 1 0 0 0 1 2 3 4 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 1 0
反正就这么处理前缀数组了
int j = 0;
for (int i = 2; i <= rr; i++) {
while (j > 0 && b[i] != b[j + 1]) j = kmp[j];
if (b[i] == b[j + 1])j++;
kmp[i] = j;
}
然后就是匹配,以j+1为模式串下标,i为目标串下标,每次匹配字符成功时共同进位,如果不成功就模式串下标向前寻找上一个“头”的“屁股”,这样可以保证回头找的时候每个“头”都与目标串在“头”长度之前的所有字符能够匹配,从而减少时间
之前啦个字符串
abcdabcdxabcdabcd
前缀数组为
0 0 0 0 1 2 3 4 0 1 2 3 4 5 6 7 8
假设他匹配的目标串当前是
abcdabcdxabcdax……
abcdabcdxabcdabcd
|-> j=14
此时j+1=15位无法匹配,则 j =数组[ j ]=5
变为
abcdabcdxabcdax……
abcdabcdxabcdabcd
|-> j=5
j+1=6,还是不行,重复上面操作,j=0,
j+1=1,还是匹配不上,溜了,让i++去(
j = 0;
for (int i = 1; i <= ll; i++) {
while (j > 0 && a[i] != b[j + 1]) j = kmp[j];
if (a[i] == b[j + 1])j++;
if (j == rr) {
printf("%d\n", i - rr + 1);
j = kmp[j];
}
}
ps:输入字符串时地址+1是学习题解里的办法,自己用地址原位写,try了半天没整出来
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<malloc.h>
char a[1000009], b[1000009];
int kmp[1000009] = {0};
int main() {
scanf("%s%s", a + 1, b + 1);
int ll = strlen(a + 1), rr = strlen(b + 1);
int j = 0;
for (int i = 2; i <= rr; i++) {
while (j > 0 && b[i] != b[j + 1]) j = kmp[j];
if (b[i] == b[j + 1])j++;
kmp[i] = j;
}
j = 0;
for (int i = 1; i <= ll; i++) {
while (j > 0 && a[i] != b[j + 1]) j = kmp[j];
if (a[i] == b[j + 1])j++;
if (j == rr) {
printf("%d\n", i - rr + 1);
j = kmp[j];
}
}
// for (int i = 1; i <= rr; i++) printf("%d ", kmp[i]);
return 0;
}
Hash算法可以将一个数据转换为一个标志,这个标志和源数据的每一个字节都有十分紧密的关系。Hash算法还具有一个特点,就是很难找到逆向规律。(百度百科)
这个题目模板感觉是要求尝试求出每个字符串的唯一“编号”,然后以这个唯一“编号”来判断
(随便找个质数当进制,怎么取模听电脑由命)
(用快排还能寄是我没想到的)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<malloc.h>
char all[10005][1505];
unsigned long long int num[10005]={0};
void qqsort(int op,int ed);
int main()
{
int n,l;
scanf("%d",&n);getchar();
for(int i=0;i<n;i++){
scanf("%s",all[i]);
l=strlen(all[i]);
for(int j=0;j<l;j++) num[i]=num[i]*13331+all[i][j];
}
unsigned long long int k;
for(int i=0;i<n;i++){
for(int j=i;j<n;j++){
if(num[i]<num[j]){
k=num[i];num[i]=num[j];num[j]=k;
}
}
}
int end=1;
for(int i=1;i<n;i++){
if(num[i]!=num[i-1]) end++;
}
printf("%d",end);
return 0;
}