最小表示法(模板)

最小表示法就是对于一个循环字符串,其字典序最小的状态;

显然任意一个循环串的最小表示法是唯一的,那么可以同过比较两个循环串的最小表示法来判断它们是否相同;

对于朴素算法:

初始化:i = 0, j = 1, k = 0;

若 s[i] < s[j],j++;

若 s[i] > s[j],i = j, j++;

若 s[i] == s[j],则 k++,直至 s[i + k] != s[j + k]

  对于 s[i + k] < s[j + k],j++;

  否则 i = j, j++;

返回 min(i, j);

时间复杂度 O(n^2),其中 n 为字符串 s 的长度;

代码:

 1 int get_min(string s){
 2     int len = s.size();
 3     int i = 0, j = 1, k = 0;
 4     while(i < len && j < len){
 5         if(s[i] < s[j]) j++;
 6         else if(s[i] > s[j]) i = j++;
 7         else{
 8             k = 0;
 9             while(k < len && s[i + k] == s[j + k]){
10                 k++;
11             }
12             if(s[i + k] < s[j + k]) j++;
13             else i = j++;
14         }
15     }
16     return i < j ? i : j;
17 }

优化:

s[i + k] != s[j + k]时:

对于 s[i + k] > s[j + k], 可直接令 i += k + 1;

关于其正确性证明:

i += k + 1 可行,只需证明 1) 以 i ~ i + k 中字符为头首字符的串不可能字典序最小;

证明 1) ,只需证明 2) 对于以 i ~ i + k 开头,以 i + k 结尾的后缀,一定存在等长的子串字典序比其小;

证明 2): 任取 i <= i‘ <= i + k,构造子串 s[i‘, i + k],len = i  + k - i‘ + 1;取与其等长子串 s[j + k - len + 1 , j + k];

显然有:s[i‘, i + k - 1] = s[j + k - len + 1, j + k - 1] && s[i + k] > s[j + k],所以 s[i‘, i + k] > s[j + k - len + 1, j + k];

所以结论 2) 得证,即结论 1) 得证;

对于 s[i + k] < s[j + k],可令 j += k + 1 ;

其正确性证明与上面类似:

j += j + 1 可行,只需证明 1) 以 j ~ j + k 中字符为头首字符的串不可能字典序最小;

证明 1) ,只需证明 2) 对于以 j ~ j + k 开头,以 j + k 结尾的后缀,一定存在等长的子串字典序比其小;

证明 2): 任取 j <= j‘ <= j + k,构造子串 s[j‘, j + k],len = j  + k - j‘ + 1;取与其等长子串 s[i + k - len + 1 , i + k];

显然有:s[j‘, j + k - 1] = s[i + k - len + 1, i + k - 1] && s[j + k] > s[i + k],所以 s[j‘, j + k] > s[i + k - len + 1, i + k];

所以结论 2) 得证,即结论 1) 得证;

注意:若出现 i == j 的情况,则将 j 往后移一位;

时间复杂度 O(n),其中 n 为字符串 s 的长度;

代码:

 1 void get_min(int n, int m){//最小表示法
 2     int i = 0, j = 1 ,k = 0, t;
 3     while(i < m && j < m && k < m){
 4         t = a[n][(i + k) % m] - a[n][(j + k) % m];
 5         if (!t) k++;
 6         else{
 7             if(t > 0) i += k + 1;
 8             else j += k + 1;
 9             if (i == j) j++;
10             k = 0;
11         }
12     }
13     a[n][m] = i < j ? i : j;
14 }

时间: 2024-08-23 20:55:17

最小表示法(模板)的相关文章

字符串最大最小表示法模板 ( 字典序最大最小 )

模板 int getMin(char *s) { int i = 0, j = 1, l; int len = strlen(s); while(i < len && j < len) { for(l = 0; l < len; l++) if(s[(i + l) % len] != s[(j + l) % len]) break; if(l >= len) break; if(s[(i + l) % len] > s[(j + l) % len]) { if

最小标示法模板 poj1509

最小标示法:给定一个字符串,不断将其最后一个字符放到开头,最终会得到n个字符串,称这n个字符串循环同构,这些字符串中字典序最小的一个,就是最小表示法 #include<iostream> #include<cstring> #include<cstdio> using namespace std; #define maxn 1000005 int len; int main(){ int t; cin>>t; while(t--){ char s[maxn&

字符串的最大最小表示法 模板

#include<iostream> #include<string> using namespace std; /* 用最小表示法求字符串S的最小字典序 返回字典序最小的串的首字母位置 */ int minstr(char s[]) { int len=strlen(s); int i=0,j=1; while(i<=len-1 && j<=len-1) { int k=0; while(k<=len-1 && s[(i+k)%l

【最小表示法】BZOJ2882-工艺

[题目大意] 求一个循环数列的最小表示法. [思路] 最小表示法模板题.之前用SAM做的,MLE了hhhhh戳☆ 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int MAXN=300000+50; 7 int s[MAXN*2],n; 8 9 void init() 10 { 1

HDOJ3374 String Problem 【KMP】+【最小表示法】

String Problem Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1512    Accepted Submission(s): 668 Problem Description Give you a string with length N, you can generate N strings by left shifts

最大最小表示法

目的:已知一个串S,求S的循环的同构字符串S'中字典序最大/最小的一个,输出其位置,place∈[0,n-1] 设S=bcad,S'=bcad,cadb,adbc,dbca,最小表示的S'是adbc,位置是2,最大表示的S'是dbca,位置是3 时间复杂度:O(n) 自用模板: int get_minstring(char *s) //最小表示法 { int len=strlen(s); int i=0,j=1,k=0; while(i<len&&j<len&&

HDU 4162 Shape Number(字符串,最小表示法)

HDU 4162 题意: 给一个数字串(length <= 300,000),数字由0~7构成,求出一阶差分码,然后输出与该差分码循环同构的最小字典序差分码. 思路: 第一步是将差分码求出:s[i] = (s[i] - s[i+1] + 8) % 8; 第二步是求出最小字典序的循环同构差分码,我之前没注意到字符串规模..直接用set做,MLE+TLE... 正确的方式应该是一种O(n)的解法,即最小表示法.//关于最小表示法的证明与详述请参考最小表示法:) 最小表示法算法: 初始时,i=0,j=

USACO 5.5.2 字符串的最小表示法

这道题是最小表示法的一个应用, 代码如下: /* ID: m1500293 LANG: C++ PROG: hidden */ #include <cstdio> #include <algorithm> #include <cstring> using namespace std; char s[100000 + 100]; int len; int mins(char s[], int len) { int i=0, j=1, k=0; while(i<len

【枚举】【最小表示法】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem F. Matrix Game

给你一个n*m的字符矩阵,将横向(或纵向)全部裂开,然后以任意顺序首尾相接,然后再从中间任意位置切开,问你能构成的字典序最大的字符串. 以横向切开为例,纵向类似. 将所有横排从大到小排序,枚举最后切开的位置在哪一横排,将这一排提到排序后的字符串数组最前面,求个"最大表示法",如果最大表示法的位置恰好在第一排的位置,那么可以用来更新答案. 如果不在第一排的位置,那么其所构成的仍然是合法的串,而且一定不会影响答案. 这是一个最小表示法的板子. #include<cstdio>