最小表示法

本文详细介绍了最小表示法的概念及其在循环同构串中的应用,通过对比朴素算法与改进算法,展示了如何高效地找到字典序最小的循环同构串,并附上了对应的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最小表示法就是找出字符串S的的循环同构串中字典序最小的一个。

那么什么是循环同构串呢。是

--S=bcad,且S’S的循环同构的串。S’可以是bcad或者cadb,adbc,dbca

即在字符串S中从i>=0开始,从i循环到字符串末尾,再从头循环到i,所形成的字符就是S循环同构串。


因为这样的同构串不止一个,所以我们要找出其中字典序最小的一个(即字符串从小到大排序,其中最小的一个)

为什么要找出其中最小的一个呢?我也不知道诶

最小表示法其实就是找到位置i,从这个位置输出S,使得到的同构串字典序最小。

朴素算法的思想就是

让i=0;j=1(i和j<str.size());然后开始比较,即比较从i发起循环的同构串与j发起循环的同构串那一个比较小

如果

str[i]<str[j];//j++,让j=2发起循环的同构串与i=0发起的同构串比较

如果

//我们要让i发起循环的串最小
str[i]>str[j];//说明i发起循环的串大于j发起的令i=j;j=j+1;

如果

str[i] ==str[j];//就引入一个指针k,令k自加比较由i发起循环的串和j发起的串的余下字符的大小
//直到str[i+k]!=str[j+k]或者k<str.size()
//如果是因为
k<str.size();//这个原因退出循环,那么两个同构串相等,比较下一个,即令j++;
//如果是因为
str[i+k]<str[j+k];//令j++;
//如果是因为
str[i+k]>str[j+k];//令i=j;j++;

下面是代码:

//寻找字典序循环同构串
#include<iostream>
#include<string>
using namespace std;

int find_min_subs(string &str);
int main()
{
	string str;
	while(cin>>str)
	{
		int index = find_min_subs(str);
		cout<<index<<endl;
		cout<<&str[index];
		string str2(str.begin(),str.begin()+index);
		cout<<str2<<endl;
	}

}

int find_min_subs(string &str)//朴素的寻找法
{
	int i;
	int j;
	int k;
	i = 0;
	j = 1;
	k = 1;
	for(;i<str.size() && j<str.size() ;)
	{
		if(str[i] > str[j])//大于,说明以j开始的串比较小,我们已该串为标准,再找出比它还小的
		{
			i = j;
			j = j+1;
		}
		else if(str[i] < str[j])//说明j指向的串不是最小的,比较下一个
		{
			++j;
		}
		else//相等,不能判断两个字符串哪个小,比较下一位
		{
			k=1;
			while(k<str.size())
			{
				if(str[(i+k)%str.size()] == str[(j+k)%str.size()])
					k++;
				else if(str[(i+k)%str.size()] < str[(j+k)%str.size()])
				{
					j++;
					break;
				}
				else
				{
					i=j;
					j=j+1;
					break;
				}

			}
			if(k==str.size())//表明两个同构串相等,取下标比较小的那一个
				j++;
		}
	}
	return i;
	//返回从第i个字符开始时str的最小表示
}

看了下算法,发现如果输入是aaaaab时,算法效率会退化为O(n^2)

所以改进了下算法

实现方法:

(1).利用两个指针p1,p2。初始化时p1指向s[0],p2指向s[1]。

(2).k=0开始,检验s[p1+k]和s[p2+k]是否相等,相等则k++,一直下去,直到找到第一个不相同的字符(若k试了一个字符串的长度也没找到不同,即整个串都是相同的字符。则那个位置就是最小表示位置,算法终止并返回)。该过程中s[p1+k]和s[p2+k]的关系有三种:

  1).s[p1+k]>s[p2+k],p1滑动到p1+k+1处,s[p1--p1+k-1]不会是循环字符串的"最小表示"的前缀。

  2).s[p1+k]<s[p2+k],p2滑动到p2+k+1处。

  3).s[p1+k]==s[p2+k],则k++,if(k==len)返回结果。

  若滑动后p1==p2,将正在变化的那个指针在+1.直到p1,p2把整个字符串都检验完毕,返回两者中小于len的值。

(3).如果 k==len,则返回min( i , j )

  如果 p1>=len,返回 p2
  如果 p2>=len,返回p1当中最小的
}


int find_min_subs(string &str)//改进的算法
{
	int i = 0;
	int j = 1;
	int k = 0;
	int len = str.size();
	while(i<len && j<len && k<len )
	{
		int t = str[(i+k)%len] - str[(j+k)%len];
		if(t == 0)
			k++;
		else
		{
			if(t>0)
				i+=(k+1);
			else
				j+=(k+1);
			if(i == j)
				j++;
			k=0;
		}
	}
	return i<j?i:j;//返回i,中最小的一个


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值