• 最小表示法


    模板题

    传送门

    很疑惑怎么就是蓝题了,这算法不难理解呀(比KMP好多了)

    概念

    给定一个字符串s,不断把s的最后一个元素放到开头,可以得到n个字符串,其中字典序最小的一个称为s的最小表示.

    另外,这n个字符串被称作是循环同构的,为了方便叙述,这里定义b[]存储s的循环同构字符串,且b[i]表示以s的第i位开头的字符串

    朴素算法

    根据定义,枚举所有的b,逐位匹配找出最小值,复杂度O(n^2)

    优化算法

    首先,将s变化为:s=s+s,得b[i]=s.substr(i,s.size())(substr意义同C++string)

    对于任意i,j,在s.substr(i,k-1)==s.substr(j,k-1)的前提下,若s[i+k] > s[j+k],显然,b[i]不是最小表示.此外,b[i+1],b[i+2]...b[i+k]均不是最小表示

    原因:

    对于p(1<=p<=k),存在一个比b[i+p]更小的循环同构串:b[j+p](这里不太好理解,但是仔细想想,或者搞个字符串模拟一下,还是可以理解的)

    因此,每次找到s[i+k] > s[j+k]时,i=i+k+1,若得到的i等于j,则++i

    时间复杂度

    结论:O(n)

    证:

    若每次比较从前向后扫描了k的长度,则i或j二者之一会向后移动k,而i和j合计一共最多向后移动2n的长度,因此时间复杂度为O(n)

    模板题代码

    #include <iostream>
    #include <cstdio>
    using namespace std;
    int read() {
    	int re = 0;
    	bool sig = false;
    	char c = getchar();
    	while(c < '0' || c > '9') {
    		if(c == '-')sig = true;
    		c = getchar();
    	}
    	while(c >= '0' && c <= '9')
    		re = (re << 1) + (re << 3) + c - '0',
    		c = getchar();
    	return sig ? -re : re;
    } 
    int n ; 
    int a[600010];
    int main() {
    	n = read();
    	for(int i = 1 ; i <= n ; i++)
    		a[i] = a[n + i] = read();
    	
    	int i = 1 , j = 2 , k;
    	while(i <= n && j <= n) {
    		for(k = 0 ; k < n && a[i + k] == a[j + k] ; k++);
    		if(k == n)break;
    		if(a[i + k] > a[j + k]) {
    			i = i + k + 1;
    			if(i == j)++i;
    		}
    		else {
    			j = j + k + 1;
    			if(i == j)++j;
    		}
    	}
    	int ans = (i < j ? i : j);
    	for(int p = ans ; p < ans + n ; p++)
    		printf("%d " , a[p]); 
    	return 0;
    }
    
  • 相关阅读:
    1052: 最大报销额
    1036: 小希的数表
    1050: 找出直系亲属
    1048: 导弹防御系统
    1051: 魔咒词典
    以大数据眼光欣赏唐人文墨(一)
    Java 内部类详解
    那些“不务正业”的IT培训公司
    Brackets 前端编辑器试用
    Emmet 快速编写html代码
  • 原文地址:https://www.cnblogs.com/dream1024/p/14001073.html
Copyright © 2020-2023  润新知