关于KMP的拓展,其实我感觉过程有点类似
能理解KMP之前的next数组更好,不理解也可以看
图文并茂:
大佬的讲解
HDU 4300
这道题的题意窝看了好几篇博客才看懂,然后看懂也做不粗来。。。。。
这里说一下吧。
题意:
给两个字符串,第一行字符串是作为密文的字典,第二行就可能是密文+明文(明文可能没有)然后将密文转为明文,即输出格式为:密文+明文(这里的明文就是密文翻译过来的),尽可能少的添加字母并使密文与明文一一对应。
还是举个栗子吧:
qwertyuiopasdfghjklzxcvbnm
qwertabcde
这里第一行的qwert就代表着“abcde”(后面的以此类推),第二行如果将qwert看做密文,就需要翻译成abcde
这时候会发现“qwert”后面刚好就是我们所需要的“abcde”这样就不用去添加字母了,那么这个假设就成为了我们的最优解
思路:
因为密文一定存在,则将密文翻译成明文之后的长度就是原有密文长度的两倍,即密文在所给的字符串中至少占有
一半。所以直接用map映射第二行的字符串,然后作为模式串与第二行字符串(即文本串)求出extend数组(extend数组存放的值代表从第i位开始到末端与文本串相同的前缀)
#include<iostream>
#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<map>
#pragma warning (disable :4996)
using namespace std;
#define maxn 100010
/* 求解T中next[],注释参考GetExtend() */
void GetNext(char* T, int nex[])
{
int t_len = strlen(T);
nex[0] = t_len;
int a;
int p;
for (int i = 1, j = -1; i < t_len; i++, j--)
{
if (j < 0 || i + nex[i - a] >= p)
{
if (j < 0)
p = i, j = 0;
while (p < t_len&&T[p] == T[j])
p++, j++;
nex[i] = j;
a = i;
}
else
nex[i] = nex[i - a];
}
}
/* 求解extend[] */
void GetExtend(char* S, char*T, int extend[], int next[])
{
GetNext(T, next); //得到next
int a;
int p; //记录匹配成功的字符的最远位置p,及起始位置a
int s_len = strlen(S);
int t_len = strlen(T);
for (int i = 0, j = -1; i < s_len; i++, j--) //j即等于p与i的距离,其作用是判断i是否大于p
//(如果j<0,则i大于p)
{
if (j < 0 || i + next[i - a] >= p) //i大于p(其实j最小只可以到-1,j<0的写法方便读者理解程序),
{ //或者可以继续比较(之所以使用大于等于而不用等于也是为了方便读者理解程序)
if (j < 0)
p = i, j = 0; //如果i大于p ,p为从i开始匹配成功的最远距离
while (p < s_len&&j < t_len&&S[p] == T[j])
p++, j++;
extend[i] = j;
a = i; //起始位置
}
else
extend[i] = next[i - a];
}
}
map<char, char>q;
int main()
{
int nex[maxn] = { 0 };
int extend[maxn] = { 0 };
char a[maxn], b[maxn], c[maxn];
int T;
scanf("%d", &T);
while (T--) {
scanf("%s%s", &a, &b);
int len = strlen(a);
int len1 = strlen(b);
for (int i = 0; i < len; i++)
q[a[i]] = 'a' + i; //映射后存入map
for (int i = 0; i < len1; i++)
c[i] = q[b[i]];//映射
c[len1] = '\0';
GetExtend(b, c, extend, nex);
/*for (int i = 0; i < len; i++)cout << extend[i]<<' ';
cout << endl;*/
int k;
for (k = 0; k <len1; k++) {
if (k + extend[k] >= len1 && k >= extend[k])//k代表密文长度
break;
}
for (int i = 0; i <k; i++)cout << b[i];
for (int i = 0; i <k; i++)cout << q[b[i]];
cout << endl;
}
return 0;
}
CCPC网络赛
A Secret
思路:
因为题目原本是要找后缀。
但是kmp拓展是关于前缀的,所以将字符串翻转。
再由extend数组的等差之和(取模后),则为所求答案
然后直接贴代码吧。代码是直接搬运的没多少改动 ̄□ ̄||
#pragma warning (disable :4996);
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <string>
#include <stack>
#include <cmath>
#include <queue>
#include <set>
#include <map>
using namespace std;
typedef long long ll;
const int N = 1e6 + 7;
const ll mod = 1000000007LL;
char s[N], t[N];
int ne[N], extend[N];
void getnxt(char T[], int len, int nxt[]){
nxt[0] = len;
int a;
int p;
for (int i = 1, j = -1; i < len; i++, j--){
if (j < 0 || i + nxt[i - a] >= p){
if (j < 0)
p = i, j = 0;
while (p < len && T[p] == T[j])
p++, j++;
nxt[i] = j;
a = i;
}
else
nxt[i] = nxt[i - a];
}
}
void getextend(char S[], char T[], int ls, int lt, int extend[], int nxt[])
{
getnxt(T, lt, nxt); //得到next
int a;
int p; //记录匹配成功的字符的最远位置p,及起始位置a
for (int i = 0, j = -1; i < ls; i++, j--) //j即等于p与i的距离,其作用是判断i是否大于p(如果j<0,则i大于p)
{
if (j < 0 || i + nxt[i - a] >= p) //i大于p(其实j最小只可以到-1,j<0的写法方便读者理解程序),
{ //或者可以继续比较(之所以使用大于等于而不用等于也是为了方便读者理解程序)
if (j < 0)
p = i, j = 0; //如果i大于p
while (p < ls && j < lt && S[p] == T[j])
p++, j++;
extend[i] = j;
a = i;
}
else
extend[i] = nxt[i - a];
}
}
int main(void)
{
int T, i;
scanf("%d", &T);
while (T--)
{
scanf("%s%s", s, t);
int la = strlen(s);
int lb = strlen(t);
//翻转
reverse(s, s + la);
reverse(t, t + lb);
getnxt(t, lb, ne); //获取next 数组在下一函数做实参
getextend(s, t, la, lb, extend, ne);
ll ans = 0;
for (i = 0; i < la; ++i)
{
ll temp = (ll)extend[i] * ((ll)extend[i] + 1) / 2;//等差求结果
ans = (ans + temp) % mod;
}
printf("%I64d\n", ans);
}
return 0;
}