上午
题目如下:
kmp的模板题,因为next数组的值没有设定好,导致求最长border的时候出现问题。后来修改了next的值的初始设定,然后所谓的border就是直接输出next数组里的值就好了。
就像之前说过的,kmp利用字符串的对称性来节省时间复杂度,next数组中的值其实就是当两字符串进行字符匹配的时候,如果失配了,该从哪个下标位置重新开始匹配。而我们的next值是根据i字符前所构成的字符串中前后缀相同的个数,比如:abab,此时next[4]=2(下标从0开始)。使用kmp的话,当abab后的那个字符失配了,我们就直接从下标为2的字符开始匹配,而不会是再接着之前初始的位置的下一个,这样就节省了不少时间复杂度。为什么呢?你想,既然已经k到了4了,代表之前的字符都是匹配成功的,并且这匹配成功的字符串还有前后缀相同的情况,这表明前后缀也是匹配成功的,那么接着下面去匹配也不必要了,因为之前的样子是符合匹配的,如果更改了另一个字符去匹配,那么一定是错误的,就可以省略了。直接从此副字符串中的中间位置去匹配,这也是因为字符串的对称性!!
代码如下:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
int main()
{
char s1[1000010],s2[1000010];//存储主串和匹配的子串
scanf("%s%s",s1,s2);
int next[1000010]={0,0};//第一个字符没有前后缀所以为0,第2个字符如果不匹配的话也只能回到第一个
//也就是下标为0,所以先给它赋值
int l1=strlen(s1),l2=strlen(s2),k=0;
for(int i=1;i<l2;i++)
{
while(k&&s2[i]!=s2[k])//k==0的时候直接跳过,当遇到两字符不相同时,就可以使用next数组中的值进行下标调整
//直到有字符相同才跳出
k=next[k];
next[i+1]=s2[i]==s2[k]?++k:0;//当有字符相同时,我们就可以把当前最长的前后缀相同字符串长度记下
}
k=0;
for(int i=0;i<l1;i++)
{
while(k&&s1[i]!=s2[k])//开始和主串匹配,如果不匹配就使用next数组调整下标
k=next[k];
k+=s1[i]==s2[k]?1:0;//如果匹配就继续下一个字符的匹配
if(k==l2)//当匹配完成后,输出相同的初始位置
cout <<i-l2+2<<endl;
}
int i;
for(i=1;i<l2;i++)//这就是所求的i长度的最长前后缀相同个数
cout <<next[i]<<" ";
cout <<next[i];
}
下午
题目为:
一开始就想着用hash,因为看着这题目意思用哈希会挺简单的,实际上确实是这样,只是我之前钻了个牛角尖,导致写半天都是:
为什么咧?因为我用map数组记录hash值的出现次数,这就导致得要一个很大很大的数组才能把hash给记录起来,然后就内存过大了……把数组调小一点的话,就变成上面那样了。我想应该是hash值出现了冲突,然后我就一直在修改,改啊改啊,改了半天没改出来,一直在说的哪两种情况徘徊。后来我看到题目说的测试例数也就10000个,我就直接开个unsigned long long的数组,没出现一个hash值就存进去,这是map也就只需要开10000个就好了。nice!然后就过了。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
#define ull unsigned long long int
ull an[10010],mapi[10010];//之前是想要开一个极大的数组用以存储hash值出现,就类似桶排
//然后就卡了我两个小时……数组开大了就内存超限,开小点hash值就会出
//现冲突
//真是裂开,后来我看了看总共也就会出现10000个人名,所以就算我一个
//一个比较没多少时间复杂度
//于是我干脆直接开一个这样大小的数组,类型改大就可以存储了,更何
//况还可以自动溢出,mapi数组用以记录人名的出现次数
ull BKDRhash(char s[55])//BKDRhash函数转换
{
int base=1313,len=strlen(s);
ull hashi=0;
for(int i=0;i<len;i++)
hashi=(hashi*base+s[i]-'a');
return hashi;
}
int main()
{
int n,m;
char st[55],l;
cin >>n;
l=getchar();
for(int i=0;i<n;i++)
{
cin >>st;
an[i]=BKDRhash(st);
mapi[i]=1;//出现了的人名就给它标记
}
cin >>m;
l=getchar();
for(int i=0;i<m;i++)
{
cin >>st;
ull t=BKDRhash(st);
int j;
for(j=0;j<n;j++)//在已经得出n个hash值中寻找是否已经出现此hash值
//已经出现就代表
if(an[j]==t)
{
if(mapi[j]==1)//等于1代表有这个人名并没有重复点名
cout <<"OK"<<endl,mapi[j]++;
else if(mapi[j]>=2)//当等于2时,就说明已经点过一次名字了
cout <<"REPEAT"<<endl;
break;
}
if(j>=n)//此时从头到尾都没找到过此hash值的出现,说明没有这个人
cout <<"WRONG"<<endl;
}
}
晚上
接着思考kmp的原理和实现代码。
写题解。写博客。
看大话数据结构。
明日计划
写完最后一题,然后看最短路。