poj1509 最小表示法

学习一下最小表示法……,部分内容与此题无关。

1.起始问题 :比较两个字符串是否循环同构
       比如说abca和caab是循环同构的(abca和acba不是循环同构)

2.复杂度较高的算法
      例举A串的所有循环同构串 和B串作比较:abca,bcaa,caab,aabc挨个和caab比一边
      复杂度O(N^2)

   能否优化到O(N)? kmp算法显然可以,但是kmp是针对匹配的,不是专门针对同构的,显得有些麻烦

3.最小表示法,就是充分利用循环同构的特点,进行比较的一种算法,两个串如果循环同构,会有什么特点?
比如以上例举的四个abca的循环同构串abca,bcaa,caab,aabc,如果有一种映射,这四个字符串对应的映射值相同,那比较起来就太快了

我们利用字符串的字典序,把这四个都映射到aabc,那就可以很快比较了,具体还有一些细化,利用最小表示的起始位置等等


具体的算法和证明, 周源2003年国家队论文写的不错


4.最小表示的最靠近串首的起始位置计算

abca的同构串中aabc最小,那么要记录下aabc开头的a在abca中的位置,就不用记录abca了,所以只要算出这个位置,就可以进行字符串之间循环同构的比较
    复杂度O(N)

我参考了这篇博客http://blog.csdn.net/zy691357966/article/details/39854359




对于一个字符串,i开始指向0,j开始指向1,算法的过程中,i和j中小的数表示当前认为的合理位置,大的数表示从这个数开始可能会找到更合理的位置
(开始认为从最开始的位置是合理的 i= 0,只有在0后面才可能找到更合理的,所以j=1)
然后把字符串复制一遍到末尾(方便循环)

每次从i+0和j+0开始比较,如果一直比较串长度个,都一样,那么恭喜你,i和j中的最小者就是答案了(为什么自己想(实际上是我懒,要结合i和j所代表的意义))

如果比较到i+k和j+k不相等,结束循环  。

【下面这段是关键】
s(i,i+k-1)的每一位和s(j,j+k-1)的每一位对应相等,而如果s[i+k] > s[j+k],则i到s(i+x,i+k)显然比s(j+x,j+k)大,(0<=x<=K)
那么最小表示的开始位置就不可能是i到i+k【s(x,y)表示x到y的字符串】

所以这个时候把i置为i+k+1,类似的,如果s[j+k] > s[i+k]就把j置为j+k+1
【上面那段是关键】

如果某一次加完之后i和j相等,那就和开始的时候一样,不好比较了,这个时候把i或者j+1,继续算法

每一次这样的循环结束后,如果i或者j超出m的范围了,那么另外一个就是解了!

上一下自己的代码……

#include
#include
#include
#include
using namespace std;
int  minpre(char s[])
{
	int m = strlen(s);
	memcpy(s + m, s,m*sizeof(char));
	int i = 0,j = 1;//i表示从i往后是有可能的,j表示下一个试探的位置 
	while(j < m && i < m) 
	{
		int k;
		for (k = 0;s[i + k] == s[j + k] && k < m; k++)
		if(k == m ) return min(i,j);//如果相等了,那最小的就是答案 
		if(s[i + k] > s[j + k]) i = i + k + 1;//i到i+k开始的都不可能 
		else (j = j + k + 1);//同上 
	    if (i == j) j++;//刚好相等,重新比较 
	    
	}
	if(i < m) return i;
	else return j;
}

int main()
{
    char s[20005];
	int n;
	scanf("%d",&n);
	while(n--)
	{
		scanf("%s",s);
	    printf("%d\n",minpre(s) + 1);
	}	
} 

第一次发博客,小紧张……有点捉急


















你可能感兴趣的:(acm)