您的位置:首页 > 其它

hihocoder 第三周

2015-08-18 11:07 155 查看

提示二:NEXT数组的使用

“那么,为了能够充分理解NEXT数组,我们再来回顾一下如何使用NEXT数组~"小Hi摆出一副老师的样子,说道。”首先我们来给出NEXT数组的数学定义~“

NEXT[0] = -1NEXT[i] = max{ 0<=k< i | str.substring(1, k) == str.substring(i - k +1 , i) } 其中str.substring(i, j)表示str从位置i到位置j的子串,如果i>j则,substring为空

”那么我们对之前例子中的模式串进行求解,可以得到这样的NEXT数组。“小Hi在纸上写了又写,画了又画。

模式串:

b a b a b b

NEXT:

0 0 1 2 3 1

”然后再来看这个NEXT数组是如何使用的!为了表明NEXT的所有使用情况,我们换一个原串。然后首先,我们第一次匹配,如果 用ori表示原串,用par表示模式串,用p表示原串的下标(从1开始),用q表示模式串的下标(从1开始)的话,会发现最多匹配到p=5, q=5就不能往下匹配了,因为此时ori[p +1]不等于par[q + 1]“小Hi为了使说明更加简洁,先下了一堆定义。

”好的!小Hi老师好棒!“小Ho在一旁煽风点火道。

原串(p=5):

babab | abcbababababb

模式串(q=5):

babab | b

”此时,令q = NEXT[q],并将ori[1..p]和par[1..q]对齐,便会发现ori[1..p]和par[1..q]仍然是一一对应的。“

原串(p=5):

babab | abcbababababb

模式串(q=3):

bab | abb

“此时,ori[p+1]和par[q+1]相同了,于是可以继续往下匹配,但是到了p=7,q=5的时候又发现不能够接着匹配了。”

原串(p=7):

bababab | cbababababb

模式串(q=5):

babab | b

”此时,令q = NEXT[q],并将ori[1..p]和par[1..q]对齐,便会发现ori[1..p]和par[1..q]仍然是一一对应的,这和之前是一样的。”

原串(p=7):

bababab | cbababababb

模式串(q=3):

bab | abb

“此时,ori[p+1]和par[q+1]仍然不相同,于是还得令q=NEXT[q]。”

原串(p=7):

bababab | cbababababb

模式串(q=1):

b | ababb

“此时,ori[p+1]和par[q+1]仍然不相同,令q=NEXT[q]。”

原串(p=7):

bababab | cbababababb

模式串(q=0):

| bababb

“此时,ori[p+1]和par[q+1]仍然不相同,令q=NEXT[q]。”

原串(p=7):

bababab | cbababababb

模式串(q=-1):

| bababb

”到了这一步,就相当于我们之前所说的模式串与原串的对齐点(即枚举的原串中的起点位置)越过了这条线(当时指C右侧的那条线)的情况,这种情况下,就应当p和q均+1,然后继续之前的操作。”小Hi擦了一把汗,说道。

“这样一说,我就大致能够理解NEXT数组是怎么用来求解模式匹配问题的了,但是它是如何求的呢?一般的方法不是要O(模式串长度的立方)的么?”小Ho问道。

“这就是我接下来要和你说的啦!”小Hi笑道:“但是让我先喝口水!”



提示三:如何求解NEXT数组

“首先我们不想如何求整个NEXT数组,而是假设我们已经知道了之前例子中模式串的NEXT[1..4],来求NEXT[5]如何?”小Hi建议道。

“好的!这样我们就只需要平方级的算法就可以算出它的值了!”小Ho高兴道。

“有点追求好不好!”小Hi深深的吸了一口气:“你这样和之前的解法有什么不同么!”

“似乎没有。。那你说怎么算吧!我反正脑子已经成浆糊了。”小Ho郁闷道。

“我们把par.substring(1, 5)当做新的原串ori_new,然后把par.substring(1, 4)当做新的模式串par,会如何?”小Hi微微一笑。

“会。。我来试试!"小Ho接过小Hi手中的纸笔,便开始演算:“首先就直接匹配到了p=4, q=4的情况,这时候严格来说已经算匹配完成了,但是肯定不是就这么结束的,此时par_new[q +1]因为是空字符,所以肯定和ori_new[p+1]匹配不上。于是令q = NEXT[q]”

原串(p=4):

baba | b

模式串(q=4):

baba |

原串(p=4):

baba | b

模式串(q=2):

ba | b

”然后这时候ori_new[p + 1]就直接和par_new[q + 1]匹配上了,于是新的p=5,q=3,莫非……这个最后的q就是NEXT[5]!“小Ho忽然灵光一闪。

”没错,就是这样!那你想想现在如何求NEXT[6]。“小Hi继续引导小Ho。

”首先我们没有必要重新从头开始匹配,直接在原串和模式串的后面加上第6个字符就可以了。“小Ho分析道。

原串(p=5):

babab | b

模式串(q=3):

bab | abb

”没法继续匹配,于是令q=NEXT[q]。“

原串(p=5):

babab | b

模式串(q=1):

b | ababb

”还是没法继续匹配,于是令q=NEXT[q]。“

原串(p=5):

babab | b

模式串(q=0):

| bababb

”此时可以匹配了,新的p=6,q=1,所以NEXT[6]就是1!“小Ho高兴道:”没想到NEXT数组的本身会用一种递归的方式进行求解,真是太巧妙了!“

”那你要不要赶紧去写一下代码,KMP算法的代码可是可以写的很短很巧妙的哦!~“小Hi建议道。

”好!“

/*
 * next[]的含义: x[i-next[i]...i-1]=x[0...next[i]-1]
 * next[i]为满足x[i-z...i-1]=x[0...z-1]的最大z值(就是x的自身匹配)
 */
void kmp_pre(char x[],int m,int next[])
{
    int i,j;
    j = next[0] = -1;
    i = 0;
    while(i < m)
    {
        while(-1 != j && x[i] != x[j]) 
            j = next[j];
        next[++i] = ++j;
    }
}
int next[10010];
int KMP_Count(char x[],int m,char y[],int n)
{//x是模式串, y是主串
    int i, j;
    int ans = 0;
    kmp_pre(x, m, next);
    i = j = 0;
    while(i < n)
    {
        while(-1 != j && y[i] != x[j]) j = next[j];
        i++; j++;
        if(j >= m)
        {
            ans++;
            j = next[j];
        }
    }
    return ans;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: