HDU2752
前排膜大佬(讲解)
前排膜大佬(图)
我说一下我的理解吧。。。
还是放在代码里看着方便些( ̄□ ̄||)
这里截一个图(是大佬的)
理解这个意思就好,代码大同小异。
#pragma warning (disable :4996)
#include<stdio.h>
#include<iostream>
#include<string.h>
#define maxn 400010
using namespace std;
int nex[maxn];
void cal_next(char*str) {
int k = -1;
int len = strlen(str);
nex[0] = -1;
for (int q = 1; q < len; q++) {
while (k > -1 && str[k + 1] != str[q]) {
k = nex[k];
}
if (str[k + 1] == str[q]) {
k++;
}
nex[q] = k;
}
}
int main() {
char a[maxn];
int ans[maxn];
int alen, temp;
int i=1;
while (scanf("%s",&a)!=EOF) {
if (a[0]=='.')break;
cal_next(a);
alen = strlen(a);
ans[alen - i] = nex[alen - 1]+1; //这里直接找next最后一个数的,这样前缀和后缀必然相同
//(然后判断前缀或者后缀中是否还含有相同的片段,即前缀的前缀和(前缀的)后缀是否相同)
temp = alen - 1;
for (;;) { //如此反复可得结果
if (nex[temp] >=0){
ans[alen - i] = nex[temp]+1;
temp = nex[temp];
}
else break;
i++;
}
for ( i =alen-i+1; i <alen; i++) { //输出答案
printf("%d ", ans[i]);
}
printf("%d\n",alen); //最后输出字符串本身
memset(a, 0, sizeof(a));
memset(nex, 0, sizeof(nex));
i = 1;
}
return 0;
}
本准备语法高亮的,但是看起来比较难受(chou)
就这样吧。。
HDU2594
这个题其实就是看对next的理解,能不能想到将两个文本串拼接在一起,(将后一个文本串接在第一个文本串的后面)这样就能进行前缀和后缀的比较了。还以一个点就是在进行next的计算后,所得到的(前后缀)相同长度不能超过所给较短的那个字符串的长度。
还有就是输出有空格。。。
虽然都想到了,但是因为直接将cal_nex函数的返回值放在for循环里面导致每一次都要再去函数里面计算一遍,所以就TLE。
难受的一比
仔细想来还是自己菜的一匹。
哎。。。。
话不多数上代码
#include<stdio.h>
#include<iostream>
#include<string.h>
#pragma warning (disable :4996)
#define maxn 50010
using namespace std;
int nex[maxn*2];
//注意题目数据类型,不一定是字符串!!!
int cal_nex(char*str ,int len) { //算出str的前缀和后缀相同的长度 存入next中
int k = -1, maxx=-1,mark=0; //如next[0]=-1 不存在,next[1]=0 一个相同
char ans[maxn];
memset(ans, 0, sizeof(ans));
nex[0] = -1;
int q,len1;
len1 = strlen(str);
for ( q = 1; q <len1; q++) {
while (k > -1 && str[k + 1] != str[q]) {
k = nex[k];
}
if (str[k + 1] == str[q]) {
k++;
}
nex[q] = k;
}
while (nex[q-1] >= len){//若当前的nex[]长度大于len,则继续向前找
q--;
k--;
}
//k是从-1开始,所以+1
return k+1;
}
int main() {
char a[maxn], b[maxn],c[maxn*2];
char ans[maxn]; int len,len_a,len_b;
while(cin>>a>>b){
memset(ans, 0, sizeof(ans));
strcpy(c, a);
strcat(c, b);//将两个文本串拼在一起
len_a = strlen(a);
len_b = strlen(b);
//cout << c;
if (len_a > len_b) //记录较小文本串的长度
len = len_b;
else len = len_a;
len = cal_nex(c, len);
for (int i = 0; i <len; i++)
ans[i] = a[i];
if (strlen(ans) == 0)
printf("0\n");
else printf("%s %d\n", ans,len);
}
return 0;
}
HDU3336
老规矩,先膜大佬
大佬说这个题其实是next数组的跳转???
lno
KMP+DP真的有点难受。。
看了很久,状态方程还是要自己试一遍才更容易理解。
这里的贴一下状态方程dp[i] = dp[nex[i]+1] + 1
这里dp[nex[i]+1]
是因为我的k从-1算起。
这里讲一下我的理解吧。。
dp**从1开始计数**
nex数组也是从1开始
dp[i]代表的就是文本串前(i-1)个字符中以第i个字母为结尾的个数而后面的“+1”代表自己本身
然后dp是初始化为0
大概就是这样了。。。
AC代码。。
#include<stdio.h>
#include<iostream>
#include<string.h>
#pragma warning (disable: 4996);
#define maxn 200010
#define mod 10007
using namespace std;
int nex[maxn];
void cal_next(char*str) { //算出str的前缀和后缀相同的长度 存入next中
int k = -1; //例:next[0]=-1 不存在,next[1]=0 一个相同
int len = strlen(str);
nex[0]=nex[1] = -1;//next从1开始
for (int q = 1; q <len; q++) {
while (k >-1 && str[k + 1] != str[q]) {
k = nex[k];
}
if (str[k + 1] == str[q]) {
k++;
}
nex[q+1] = k;//这里向后移一位
}/*
for (int i = 0; i < len; i++)
cout << nex[i];
*/
}
int main() {
char a[maxn];
int dp[maxn];
int T,num;
int ans;
scanf("%d", &T);
while (T--) {
ans = 0;
memset(a, 0, sizeof(a));
memset(dp, 0, sizeof(dp));
memset(nex, 0, sizeof(nex));
scanf("%d", &num);
scanf("%s", &a);
cal_next(a);
for (int i = 1; i <=num; i+=1) {//这里是从1开始
dp[i] = dp[nex[i]+1] + 1;
ans = (ans + dp[i]) % mod;//值就是每个dp[i]的和
}
printf("%d\n", ans);
}
return 0;
}