最小表示法

字符串的最小表示法

http://poj.org/problem?id=1509

 

字符串最小表示法:以某个小标开始字典序最小的串。

假设有两个下标i,j,表示如果从i和从j出发的字符串,有一个k表示字符串的长度,如果长度达到len,就表示找到最小的串。

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

s[i+k]>s[j+k]: i=i+k+1 表示以i,到i+k为起点的字符串,都不是最小字符串的前缀。

s[i+k]<s[j+k]: j=j+k+1 同理

注意:1.i和j一定是不同的。

          2.每次不等时,需要设置k为0。

简单说,就是需要几步才能成为最小字符串

当为1时已经是最小

 

 1 #include<iostream>
 2 #include<string>
 3 using namespace std;
 4 int findMin(string s)
 5 {
 6     int len = s.length();
 7     int i=0,j=1,k=0;
 8     while(i<len&&k<len&&j<len)
 9     {
10         if(s[(i+k)%len]==s[(j+k)%len])
11         {
12             k++;
13         }
14         else if(s[(i+k)%len]>s[(j+k)%len])
15         {
16             i=i+k+1;
17             k=0;
18         }
19         else
20         {
21             j=j+k+1;
22             k=0;
23         }
24         if(i==j) j++;
25     }
26     return min(i,j);
27 }
28 int main()
29 {
30     int n;
31     string s;
32     cin>>n;
33     while(n--)
34     {
35         cin>>s;
36         cout<<findMin(s)+1<<endl;
37     }
38     return 0;
39 }
View Code

上面这个更好理解

算法用法
最小表示法是我在看插头DP的时候看到的一个名词,就先来看了一下这个算法,还是蛮简单的,有一个首位相连的字符串,我们要寻找一个位置,从这个位置向后形成一个新字符串,我们需要使这个字符串字典序最小。

算法解释
我们这里要i = 0,j = 1,k = 0,表示从i开始k长度和从j开始k长度的字符串相同(i,j表示当前判断的位置)

当我们str[i] == str[j]时,根据上面k的定义,我们的需要进行k+1操作

当str[i] > str[j]时,我们发现i位置比j位置上字典序要大,那么不能使用i作为开头了,我们要将i向后移动,移动多少呢?有因为i开头和j开头的有k个相同的字符,那么就执行 i = i + k +1

相反str[i] < str[j]时,执行:j = j + k +1 

最终i和j中较小的值就是我们最终开始的位置

相反如果是最大表示法的话,我们就要求解字典序最大的字符串,那么我们只需要在执行第二或第三个操作时选择较大的那个位置较好了

模板

 1 int Get_min()
 2 {
 3     int n = strlen(s);
 4     int i = 0,j = 1,k = 0,t;
 5     //表示从i开始k长度和从j开始k长度的字符串相同
 6     while(i < n && j < n && k < n)
 7     {
 8         t = s[(i+k)%n] - s[(j+k)%n];
 9         //t用来计算相对应位置上那个字典序较大
10         if(!t) k++;//字符相等的情况
11         else
12         {
13             if(t > 0) i += k+1;//i位置大,最大表示法: j += k+1
14             else j += k+1;//j位置大,最大表示法: i += k+1
15             if(i == j) j++;
16             k = 0;
17         }
18     }
19     return i >j ?j :i;
20 }
View Code

栗子

有一个最小表示的裸题,POJ 1509

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio>
 
using namespace std;
char s[11000];
int Get_min()
{
    int n = strlen(s);
    int i = 0,j = 1,k = 0,t;
    //表示从i开始k长度和从j开始k长度的字符串相同
    while(i < n && j < n && k < n)
    {
        t = s[(i+k)%n] - s[(j+k)%n];
        //t用来计算相对应位置上那个字典序较大
        if(!t) k++;//字符相等的情况
        else
        {
            if(t > 0) i += k+1;//i位置大
            else j += k+1;//j位置大
            if(i == j) j++;
            k = 0;
        }
    }
    return i >j ?j :i;
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i = 0;i < n;i ++)
    {
        scanf("%s",s);
        printf("%d\n",Get_min()+1);
    }
}
View Code

 

posted @ 2019-01-05 16:25  DWVictor  阅读(341)  评论(0编辑  收藏  举报