kmp 朴素和升级 附 hdu 1711 3746 思想
2012-08-14 15:33
211 查看
kmp啊啊啊啊啊 我势必要ko了你啊 啊啊
写给几天以后就可能把这个算法忘记的我。。。
以abcdabd为子串
next里共有len+1个数的 -1 0 0 0 0 1 2 0 最后一个0是对应最后一个d的 虽说字符匹配的时候用不上,但有的题是能用上的~!
比如说hdu 3746 后边再说
next数组是错位相存的 但是巧妙的对应上了 上详解代码
void getnext()
{
int i=0,j=-1;next[0]=-1;//这个next[0]就是个头,因为他占一位,所以往后的数存时都是往后串了一个,这样的好处是查询kmp时,当前位置所对应的next就是他的前一个的数的实质next,而这个next值对应的也就是前一个指向的后一个。(因为正常来说,当一个字符失配时,我们是找他前一个数的失配值所指向的字符的后一个字符,用这个字符再与刚才失配的那个进行匹配)
比如 a b c d a b d
next -1 0 0 0 0 1 2 0
我们找到第二个b时失配了,找他对应的next数组 是1 那我们就找到了第一个b(有点不完美,貌似非朴素版可以改进?待我过会看看)
又如我们找到了第二个d时,失配了。。他对应的next数组是2 那我们就找到了第一个c,用c来进行匹配。(正常来说,d失配 我们就应该找他前一个也就是b的失配字符(也就是第一个b)的后一个字符(也就是c)接着来进行匹配。)
while(i!=len)//这里还要说一下,i是相当于从头到尾不变的一个指针,j相当于来回跳的那个s[i]与前边第几个(第j个)字符相匹配的那个指针。
{
if(j==-1||s[i]==s[j])//j=-1时,代表跳到头了,也就是s[i]和这几个都匹配失败,所以下边++i。
next[++i]=++j;//这里很巧妙啊 就相当于next整体后串了一下,next值也+1了。
else
j=next[j];//j开始跳了。。往s[j]他的失配点跳
}
}
我们会发现,getnext实质就是自身的kmp匹配。next我们可以理解为dian这一点与前边多少个字符相匹配(当然是错位以后的next)。一定是与最前边的字符相匹配。
再来就是子串与母串相匹配了。
int kmp()
{
int i=0,j=0;//i是母串的指针,j是子串的指针,len1为母串长,len2为子串长。
while(i!=len1)
{
if(j==-1||S[i]==s[j])//j=-1,还是那个原理,都跳到头了,都没匹配上,i++吧
{
++i;++j;
if(j==len2) return i-j+1;//都匹配完了,成功了 返回吧
}
else
j=next[j];//看到了吧,直接找next就行了 用next指向的字符与失配点进行匹配
}
return -1;//全部匹配失败。。真悲剧
}
这就是 kmp思想了 主要就是先把自身处理一下,next记录跳转点,因为据我理解就是与前边的对应点,你既然记录了,肯定是你当前这点的前n个与一开始的前n个相对应上了已经,所以可以跳了。
我们在来说 hdu 的3746
就是讲 一个串循环 还差多少个
比如 aaa 就三个循环串 不差了 0个
basdg 这是一个循环串 所以还要补 5个
adcca adcc算一个循环串,然后后边有跟了一个a 所以还差3个
abcdabcdab abcd是一个循环串,然后ab,所以还差2个
你用next数组 next[len]存的就是最后这点与前边的第几个相对应。
比如说 abcdabcdab 那最后一个next[10]就是6 那么 len-next[10],就是循环串的长度。
再来说说升级版的kmp
这个版本的其实我觉得没什么用。。可能是我太弱了,看不出其中的奥妙。。因为我觉得他能实现的朴素都能实现,而朴素能做的他却做不了
void getnext()
{
int i=0,j=-1,len=strlen(s);
next[0]=-1;
while(i!=len)
{
if(j==-1||s[i]==s[j])
{
++i;++j;
if(s[i]!=s[j]) //重点在这 看看后一个相不相匹配 若不匹配,next[i]配上失配点
next[i]=j;
else
next[i]=next[j]; //若匹配,配上上一个失配点 next[j]里放的一定是他的失配点
}
else
j=next[j];
}
}
再来看看 kmp
int kmp()
{
int i=0,j=0,lens=strlen(s),lenT=strlen(T);
while(i!=lens&&j!=lenT)
{
if(s[i]==T[j])
{
++i;++j;
}
else
{
if(next[j]==-1)
{
++i;j=0;
}
else
j=next[j];
}
}
}
写给几天以后就可能把这个算法忘记的我。。。
以abcdabd为子串
next里共有len+1个数的 -1 0 0 0 0 1 2 0 最后一个0是对应最后一个d的 虽说字符匹配的时候用不上,但有的题是能用上的~!
比如说hdu 3746 后边再说
next数组是错位相存的 但是巧妙的对应上了 上详解代码
void getnext()
{
int i=0,j=-1;next[0]=-1;//这个next[0]就是个头,因为他占一位,所以往后的数存时都是往后串了一个,这样的好处是查询kmp时,当前位置所对应的next就是他的前一个的数的实质next,而这个next值对应的也就是前一个指向的后一个。(因为正常来说,当一个字符失配时,我们是找他前一个数的失配值所指向的字符的后一个字符,用这个字符再与刚才失配的那个进行匹配)
比如 a b c d a b d
next -1 0 0 0 0 1 2 0
我们找到第二个b时失配了,找他对应的next数组 是1 那我们就找到了第一个b(有点不完美,貌似非朴素版可以改进?待我过会看看)
又如我们找到了第二个d时,失配了。。他对应的next数组是2 那我们就找到了第一个c,用c来进行匹配。(正常来说,d失配 我们就应该找他前一个也就是b的失配字符(也就是第一个b)的后一个字符(也就是c)接着来进行匹配。)
while(i!=len)//这里还要说一下,i是相当于从头到尾不变的一个指针,j相当于来回跳的那个s[i]与前边第几个(第j个)字符相匹配的那个指针。
{
if(j==-1||s[i]==s[j])//j=-1时,代表跳到头了,也就是s[i]和这几个都匹配失败,所以下边++i。
next[++i]=++j;//这里很巧妙啊 就相当于next整体后串了一下,next值也+1了。
else
j=next[j];//j开始跳了。。往s[j]他的失配点跳
}
}
我们会发现,getnext实质就是自身的kmp匹配。next我们可以理解为dian这一点与前边多少个字符相匹配(当然是错位以后的next)。一定是与最前边的字符相匹配。
再来就是子串与母串相匹配了。
int kmp()
{
int i=0,j=0;//i是母串的指针,j是子串的指针,len1为母串长,len2为子串长。
while(i!=len1)
{
if(j==-1||S[i]==s[j])//j=-1,还是那个原理,都跳到头了,都没匹配上,i++吧
{
++i;++j;
if(j==len2) return i-j+1;//都匹配完了,成功了 返回吧
}
else
j=next[j];//看到了吧,直接找next就行了 用next指向的字符与失配点进行匹配
}
return -1;//全部匹配失败。。真悲剧
}
这就是 kmp思想了 主要就是先把自身处理一下,next记录跳转点,因为据我理解就是与前边的对应点,你既然记录了,肯定是你当前这点的前n个与一开始的前n个相对应上了已经,所以可以跳了。
我们在来说 hdu 的3746
就是讲 一个串循环 还差多少个
比如 aaa 就三个循环串 不差了 0个
basdg 这是一个循环串 所以还要补 5个
adcca adcc算一个循环串,然后后边有跟了一个a 所以还差3个
abcdabcdab abcd是一个循环串,然后ab,所以还差2个
你用next数组 next[len]存的就是最后这点与前边的第几个相对应。
比如说 abcdabcdab 那最后一个next[10]就是6 那么 len-next[10],就是循环串的长度。
再来说说升级版的kmp
这个版本的其实我觉得没什么用。。可能是我太弱了,看不出其中的奥妙。。因为我觉得他能实现的朴素都能实现,而朴素能做的他却做不了
void getnext()
{
int i=0,j=-1,len=strlen(s);
next[0]=-1;
while(i!=len)
{
if(j==-1||s[i]==s[j])
{
++i;++j;
if(s[i]!=s[j]) //重点在这 看看后一个相不相匹配 若不匹配,next[i]配上失配点
next[i]=j;
else
next[i]=next[j]; //若匹配,配上上一个失配点 next[j]里放的一定是他的失配点
}
else
j=next[j];
}
}
再来看看 kmp
int kmp()
{
int i=0,j=0,lens=strlen(s),lenT=strlen(T);
while(i!=lens&&j!=lenT)
{
if(s[i]==T[j])
{
++i;++j;
}
else
{
if(next[j]==-1)
{
++i;j=0;
}
else
j=next[j];
}
}
}
相关文章推荐
- HDU - 1711 - Number Sequence,1686 - Oulipo,2087 - 剪花布条,3746 - Cyclic Nacklace (KMP基础)
- KMP入门题 Hdu 1711 2594 3746 HUST 1010 Poj 3461 2752 2406 1961 FZU 1901
- hdu 1711(KMP)
- hdu 3746(kmp)
- HDU_1711 Number Sequence(KMP)
- HDU 3746利用KMP找循环节
- hdu 1711 Number Sequence(很简单的KMP)
- hdu 1711 kmp水体
- HDU 3746 Cyclic Nacklace(KMP)
- HDU 1711:Number Sequence(KMP)
- [HDU] 1711 Number Sequence KMP
- HDU 3746(KMP)next数组的运用
- HDU-3746 Cyclic Nacklace ( kmp )
- hdu 1711 kuangbin 字符串 A KMP入门
- HDU 1711 Number Sequence(KMP)
- KMP之循环节 hdu 3746
- hdu 1711 Number Sequence(kmp)
- hdu 3746 kmp求循环节 下标从1开始
- HDU 1711 Number Sequence【KMP模板】
- HDU 3746 KMP