`

poj 2185

    博客分类:
  • KMP
 
阅读更多

题意:给你一个字符矩阵,求出它的最小覆盖子矩阵,即使得这个子矩阵的无限复制扩张之后的矩阵,能包含原来的矩阵。 即二维的最小覆盖子串。

思路:KMP很好的一道题。首先易证:最小覆盖子矩阵一定靠左上角。那么,我们考虑求出每一行的最小重复串长度,所有行的最小重复串的长度的lcm就是最小重复子矩阵的宽。然后我们对列也做相同的操作。于是我们就可以求得最小重复子矩阵的大小了。(这里要注意一点:当所得的宽大于原来的宽时,就让等于原来的宽,长也如此)。算法实现:算法的核心在于高效的求出每一行和每一列的最小重复串,这个可以最原串做一次KMP中的get_next()。(注:以上部分为转载)

代码如下:

#include<iostream>
using namespace std;
const int rMax = 10002;
const int cMax = 77;

int row, col;
char map[rMax][cMax];

int get_next_c(int r)
{        //  对每一行求最小覆盖串。
    int next[cMax], j=1, k=0;
    next[0] = -1;
	next[1]=0;
    while(j< col)
	{
        if( map[r][j] == map[r][k])
		{
			next[j+1]=k+1;
			j++;
			k++;

        }
        else if (k==0)
        {
			next[j+1]=0;
			j++;
        }
		else
			k=next[k];
    }
    return j - next[j];
}

int get_next_r(int c)
{       //  对每一列求最小覆盖串。
    int next[rMax], j=1, k=0;
    next[0] = -1;
	next[1]=0;
    while(j< row)
	{
        if( map[j][c] == map[k][c])
		{
			next[j+1]=k+1;
			j++;
			k++;
        }
		else if (k==0)
		{
			next[j+1]=0;
			j++;
		}
        else k= next[k];
    }
    return j- next[j];
}

int lcm(int a, int b)
{      //  求a,b的最小公倍数lcm。这个模板应该学。
    int mul = a * b;
    int r = a % b;
    while(r)
	{
        a = b;
        b = r;
        r = a % b; 
    }
    return mul / b;         //  最小公倍数 * 最大公约数 = a * b。
}

int main()
{
    int r, c, ans_r = 1, ans_c = 1;
    scanf("%d%d", &row, &col);
    for(r = 0; r < row; r ++)    //  改逐个输入为逐行输入,从700MS优化到100MS,汗.....
        scanf("%s", map[r]);
    for(r = 0; r < row; r ++)
	{
        ans_c = lcm(ans_c, get_next_c(r));
        if(ans_c > col)
		{         //  注意! 当所得的宽大于原来的宽时,就让等于原来的宽,WA在了这里。
            ans_c = col; 
            break;
        }
    }
    for(c = 0; c < ans_c; c ++)
	{
        ans_r = lcm(ans_r, get_next_r(c));
        if(ans_r > row)
		{
            ans_r = row; 
            break;
        }
    }
    printf("%d\n", ans_r * ans_c);
    return 0;
}

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics