chelsea0901

什么是循环字符串的最小表示法

就是对于一个字符串来说,其同构字符串中字典序最小的一个

比如说:S = bacd,则其同构字符串有 acdb、cdba、dbac,其中acdb事S串的最小表示法,因为其字典序最小

如何求字符串的循环同构的最小表示

暴力的方法是,你将所有的同构都写出来,如何拍个序,输出第一个就可以,但是这样复杂度很高,没得做

所以我们可以这样做:

  用两个指针来求,i指向最小表示的位置,j为比较指针,最终输出i,即位最小的开头位置
令i = 0,j = 1.
  如果S[i] > S[j]	说明当前i指向的位置一定不是最小表示的起点位置,j比i小,所以j相比i来说可以是最小的起点,所以修改i的值为j,再让j++
  如果说S[i] < S[j]说明当前j指向的位置一定不是最小表示的起点位置,所以让j++
  如果说S[i] = S[j],就需要在此基础上进行接下来的比较,此时需要保持i和j的位置不变,比较后面的元素,所以需要一个新的变量k来代替ij移动,达到比较的目的
  比较S[i + k]与S[j + k],如果S[i + k] = S[j + k],则让k++,继续比下去
  如果S[i + k] < S[j + k],则k++,需要继续比下去
  如果S[i + k] > S[j + k],说明i位置开头的字符串并不是最小表示,j位置开头的字符串是比他小的,所以让i = j,j++
  记得每次比较完S[i + k]与S[j + k]不同时要将k赋值为0
 最后返回i的位置即可

这样的方法也不是最优解,面对aaaaaaaaaab这类的毒瘤数据,就会被卡的死死的,因为i的指针每次只会移动1个方位,时间复杂度也很高

所以,还需要进行优化:

对于S[i + k] < S[j + k]时,说明j不是最小表示,那么我们就可以移动j = j + k + 1;同样的,对于S[i + k] > S[j + k]时,说明i不是最小表示,就让i = i + k + 1。最后返回i与j中最小的值即可(其实返回i就行了,能过,但是保险起见还是返回最小值叭

为什么呢?

拿S[i + k] < S[j + k]来说叭,其 j 到 k 中间位置的任意位置为起点都会大于以i为起点的,因为从i开头和从j开头走的前k个位置的值是相等的,所以可能的小的位置是在j + k + 1后面的

代码实现

int getmin(string s)
{
    ll m = s.size();
    int i = 0, j = 1, k = 0;
    while (i < m && j < m && k < m) {
        int t = s[(i + k) % m] - s[(j + k) % m];
        if(t == 0)k++;
        else
        {
            if(t > 0)i += k + 1;
            else j += k + 1;
            if(i == j) j++;
            k = 0;
        }
    }
    return min(i, j);
}

例题:

How many

题意:

给你一堆字符串,问你非同构字符串有几个

思路:

对每个字符串都进行求最小表示法,将其塞到set中去重

#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
#include <stdlib.h>
#include <sstream>
#include <map>
#include <set>
using  namespace std;
#define MAX 100000 + 5
typedef  long long ll;
map<string,int>mp;
set<string>se;
void getmin(string ss, ll m)
{
    int i = 0, j = 1, k = 0, t;
    while (i < m && j < m && k < m) {
        t = ss[(i + k) % m] - ss[(j + k) % m];
        if(t == 0)
            k++;
        else
        {
            if(t > 0) i += k + 1;
            else j += k + 1;
            if(i == j) j++;
            k = 0;
        }
    }
    int a = min(i, j);//取最小值
    string sss = "";
    for(int p = 0; p < m; p++)//字符串拼接,有个函数来着,但是我也不知道为什么用不了
    {
        sss += ss[(p + a) % m];
    }
    se.insert(sss);
}
int main()
{
    int n;
    string s;
    while (cin>>n) {
        se.clear();//记得清0
        for(int i = 1; i <= n; i++)
        {
            cin>>s;
            ll m = s.size();
            getmin(s, m);
        }
        cout<<se.size()<<endl;
    }
}

最大表示法

int getmin(string s)
{
    ll m = s.size();
    int i = 0, j = 1, k = 0;
    while (i < m && j < m && k < m) {
        int t = s[(i + k) % m] - s[(j + k) % m];
        if(t == 0)k++;
        else
        {
            if(t > 0)j += k + 1;
            else i += k + 1;
            if(i == j) j++;
            k = 0;
        }
    }
    return min(i, j);
}

就将else里面的那两个换个位置

分类:

算法

技术点:

相关文章: