项链(最小表示法)

题目

题目

思路

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

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

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

首先,暴力是我们每次比较\(i\)\(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);

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

但是,我们可以优化!

在这里插入图片描述

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);

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

问题一

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

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

问题二

\(k=n\)是个什么情况?

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

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

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

在这里插入图片描述

代码

#include<cstdio>
#include<cstring>
#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;
}

猜你喜欢

转载自www.cnblogs.com/zhangjianjunab/p/13383817.html
今日推荐