项链(最小表示法)

题目

题目

思路

看到这道题目我脑子里面第一个闪过的是KMP,但是看到第二问我就发现竟然是我不会的最小表示法。

首先明确一个思路,如果对于两个东西我们要确定是否相同,最好的方法就是确定一个最小的东西判断相等,例如在AcWing 156. 矩阵 中就是最小的浏览顺序,而这里则是最小的字典序,所以我们不难想到最小表示法。

什么?你说我总是讲的不好,我也没打算讲啊,这个算法OIwiki讲的挺好的,我就只做一些注释吧以及答疑吧。

首先,暴力是我们每次比较 i i i j j j开始的循环同构,把当前比较到的位置记作 ,每次遇到不一样的字符时便把大的跳过,最后剩下的就是最优解。

int k = 0, i = 0, j = 1;
while (k < n && i < n && j < n) {
  if (sec[(i + k) % n] == sec[(j + k) % n]) {
    ++k;
  } else {
    if (sec[(i + k) % n] > sec[(j + k) % n])
      ++i;
    else
      ++j;
    k = 0;
    if (i == j) i++;
  }
}
i = min(i, j);

但是这个很明显在 a a a a a . . . b aaaaa...b aaaaa...b时炸了,这个算法是 O ( n 2 ) O(n^2) O(n2)的。

但是,我们可以优化!

项链(最小表示法)_第1张图片

int k = 0, i = 0, j = 1;
while (k < n && i < n && j < n) {
  if (sec[(i + k) % n] == sec[(j + k) % n]) {
    k++;
  } else {
    sec[(i + k) % n] > sec[(j + k) % n] ? i = i + k + 1 : j = j + k + 1;
    if (i == j) i++;
    k = 0;
  }
}
i = min(i, j);

至于正确性吗,没优化的肯定是对的,优化并没有改变正确性,所以也是对的,没有毛病。

问题一

为什么是最后是选择 m i n ( i , j ) min(i,j) min(i,j)呢?

首先看看退出的条件,那么在 i , j i,j i,j跳出 n n n的范围是,很明显 m i n min min会选另外一个,但是 k = n k=n k=n时呢?说明两个都是最小表示,随便选一个都可以。

问题二

k = n k=n k=n是个什么情况?

说明同时有两个位置都是最小表示法,那么这个字符串肯定是由一个循环节循环组成的。

有人会问会不会是 n n n个循环节加上半个循环节呢?

不会的,你画个图就会发现这样子的话 k k k是不可能等于 n n n的。

项链(最小表示法)_第2张图片

代码

#include
#include
#define  N  2100000
using  namespace  std;
char  a[N],b[N];
int  n;
int  zxbsf(char  *s)//求最小表示法的位置
{
	int  i=1,j=2,k=0;
	while(i<=n  &&  j<=n  &&  k<n)
	{
		if(s[i+k]==s[j+k])k++;
		else
		{
			if(s[i+k]>s[j+k])i=i+k+1;
			else  j=j+k+1;
			if(i==j)j++;
			k=0;
		}
	}
	return  i<=n?i:j;//其实和min大同小异。
}
int  main()
{
	scanf("%s",a+1);scanf("%s",b+1);n=strlen(a+1);
	for(int  i=2*n;i>n;i--)a[i]=a[i-n],b[i]=b[i-n];//复制一份
	int  x=zxbsf(a),y=zxbsf(b);
	int  t=0;
	for(int  i=0;i<n;i++)
	{
		if(a[x+i]!=b[y+i])
		{
			t=1;
			break;
		}
	}
	if(t==1)printf("No\n");
	else
	{
		printf("Yes\n");
		for(int  i=0;i<n;i++)printf("%c",a[x+i]);
		printf("\n");
	}
	return  0;
}

你可能感兴趣的:(技巧,题解)