字符串最小表示初探 By cellur925

我们考虑有一个字符串,可以从这个字符串的不同位置出发,把这个字符串大声朗读出来,当到字符串末端的时候再从头开始读,直到回到“梦开始的地方”。

设字符串长度为\(n\),那么有\(n\)种不同的读法。我们现在想要在这些读法中找一个字符串使得他字典序最小,如何快速求出?

我们当然可以用其他朴素的方法(这里不再赘述),但其实我们有更高效的线性算法:最小表示法。

算法描述

  1. 首先把这个字符串复制二倍接在后面(类似断环为链)
  2. 然后利用两个指针\(i=1\)和\(j=2\)在\(k=0\)的帮助下向后扫,当遇到\(i+k\)和\(j+k\)位置的字符不相等时,就退出。
  3. 如果\(i+k\)位置更大一些,直接把\(i\)跳到\(i+k+1\)。因为可以证明从\(i+1\)到\(i+k\)都不是字符串的最小表示,扫这部分就是冗余的。
  4. 两个指针不断尝试向后移动,一个移动到结尾就停止扫描,保证复杂度在\(O(n)\)内。
void work()
{
    n=strlen(str+1);ans=0;
    for(int i=1;i<=n;i++) str[n+i]=str[i];
    int i=1,j=2,k;
    while(i<=n&&j<=n)
    {
        for(k=0;k<=n&&str[i+k]==str[j+k];k++);
        if(k>=n) break;
        if(str[i+k]>str[j+k])
        {i=i+k+1;if(i==j) i++;}
        else
        {j=j+k+1;if(i==j) j++;}
    }
    ans=min(i,j);
    printf("%d\n",ans);
}

两道新鲜热乎的例题

例1:bzoj1398寻找主人

给你两个字符串,问你他们是否同构,若同构输出最小表示。

第二问是裸题,第一问我们只要分别求出两个字符串的最小表示看他们是否相同即可。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>

using namespace std;

bool flag;
int pos1,pos2,len;
char s1[3000000],s2[3000000];

void work1()
{
    len=strlen(s1+1);
    for(int i=1;i<=len;i++) s1[i+len]=s1[i];
    int i=1,j=2,k;
    while(i<=len&&j<=len)
    {
        for(k=0;k<=len&&s1[i+k]==s1[j+k];k++);
        if(k>=len) break;
        if(s1[i+k]>s1[j+k])
        {
            i=i+k+1;
            if(i==j) i++;
        }
        else
        {
            j=j+k+1;
            if(i==j) j++;
        }
    }
    pos1=min(i,j);
}

void work2()
{
    for(int i=1;i<=len;i++) s2[i+len]=s2[i];
    int i=1,j=2,k;
    while(i<=len&&j<=len)
    {
        for(k=0;k<=len&&s2[i+k]==s2[j+k];k++);
        if(k>=len) break;
        if(s2[i+k]>s2[j+k])
        {
            i=i+k+1;
            if(i==j) i++;
        }
        else
        {
            j=j+k+1;
            if(i==j) j++;
        }
    }
    pos2=min(i,j);
}

int main()
{
    scanf("%s",s1+1);
    scanf("%s",s2+1);
    work1();work2();
    int i=pos1,j=pos2,cnt=1;
    while(cnt<=len)
    {
        if(s1[i]==s2[j]) i++,j++,cnt++;
        else {flag=1;break;}
    }
    if(flag)
    {
        printf("No\n");
        return 0;
    }
    else printf("Yes\n");
    cnt=1;i=pos1;
    while(cnt<=len) cout<<s1[i],i++,cnt++;
    return 0;
}

例2 poj1509

给你很多字符串,求出他们最小表示的起点位置。

真·裸题。

#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;

int n,Q,ans;
char str[30000];

void work()
{
    n=strlen(str+1);ans=0;
    for(int i=1;i<=n;i++) str[n+i]=str[i];
    int i=1,j=2,k;
    while(i<=n&&j<=n)
    {
        for(k=0;k<=n&&str[i+k]==str[j+k];k++);
        if(k>=n) break;
        if(str[i+k]>str[j+k])
        {i=i+k+1;if(i==j) i++;}
        else
        {j=j+k+1;if(i==j) j++;}
    }
    ans=min(i,j);
    printf("%d\n",ans);
}

int main()
{
    scanf("%d",&Q);
    while(Q--)
    {
        scanf("%s",str+1);
        work();
    }
    return 0;
}

感觉这种算法可扩展性不太强(?),不过当个暴力工具就好了\(qwq\)。

原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9887155.html

时间: 2024-11-08 23:37:49

字符串最小表示初探 By cellur925的相关文章

字符串最小编辑距离

首先介绍一下概念 字符串编辑距离(Edit Distance),是俄罗斯科学家 Vladimir Levenshtein在1965年提出的概念,又称 Levenshtein距离,是指两个字符串之间,由一个转成另 一个所需的最少编辑操作次数.许可的编辑操作包括 1.将一个字符替换成另一个字符 2.插入一个字符 3.删除一个字符 可以借鉴LCS的思想,采用动态规划,维护一个c[m][n]二维数组,m,n的值分别为字符串1的长度+1,字符串2的长度+1. c[0][0]表示的是二空串的编辑距离,明显为

[coj 1353 Guessing the Number]kmp,字符串最小表示法

题意:给一个字符串,求它的最小子串,使得原串是通过它重复得到的字符串的一个子串. 思路:先求最小长度,最小循环长度可以利用kmp的next数组快速得到,求出长度后然后利用字符串最小表示法求循环节的最小表示即可. #pragma comment(linker, "/STACK:10240000") #include <map> #include <set> #include <cmath> #include <ctime> #include

迭代加深搜索求字符串最小周期

1 //==================================================== 2 //迭代加深搜索求字符串最小周期: 3 //==================================================== 4 5 #include <stdio.h> 6 #include <Windows.h> 7 #include <stdlib.h> 8 9 int main() 10 { 11 char *str; 1

没有来源的题(似乎是什么POI?) 字符串——最小表示法

[题目描述]? 给你两个长度为 \(n\) 的字符串,问能否通过将某一字符串的一个前缀接到该串的后面使得两个字符串相等.若可以,你还可能被要求输出通过上述操作所能得到的字典序最小的字符串. [输入格式] ? ? 第一行两个整数 \(n,T\). ? 接下来两行,每行一个长度为 \(n\) 的字符串. [输出格式] ? 若可以,输出 \(TAK\),否则输出 \(NIE\).如果 \(T=1\),你还需要在下一行输出字典序最小的字符串. [数据范围] \(n \le 1000000\) 首先,什么

POJ--2406Power Strings+KMP求字符串最小周期

题目链接:点击进入 事实上就是KMP算法next数组的简单应用.假设我们设这个字符串的最小周期为x 长度为len,那么由next数组的意义,我们知道len-next[len]的值就会等于x.这就是这个题目的关键点. 代码例如以下: #include<iostream> #include<cstdio> #include<cstring> using namespace std; const int maxn=1000000+100; char str[maxn]; in

ZOJ 1729 Hidden Password (字符串最小表示)

以前听过,不知道是什么,其实就是字符串首尾相连成一个环,n种切法求一个字典序最小的表示. 朴素算法大家都懂.O(n)的算法代码非常简单,最主要的思想是失配的时候尽可能大的移动指针. 另外附上一个不错的字符串算法总结:http://duanple.blog.163.com/blog/static/709717672009825004092/ #include<bits/stdc++.h> using namespace std; const int maxn = 1e5+5; char s[ma

KMP解决字符串最小循环节相关问题

经典问题 : 给出一个由某个循环节构成的字符串,要你找出最小的循环节,例如 abababab 最小循环节当是 ab ,而类似 abab 也可以成为它的循环节,但并非最短. 分析 : 对于上述问题有两个结论 如果对于next数组中的 i, 符合 i % ( i - next[i] ) == 0 && next[i] != 0 , 则说明字符串循环,而且 循环节长度为:    i - next[i] 循环次数为:       i / ( i - next[i] ) 水平有限,用自己的语言描述怕

hdu1358 KMP求字符串最小循环节

对于一个字符串S,长度为L,如果由长度为len的字符串s(字符串s的最小循环节是其本身)循环k次构成,那么字符串s就是字符串S的最小循环节 那么字符串有个很重要的性质和KMP挂钩,即  i - next[i] 为字符串s的长度 i%(i - next[i]) ==0 证明:字符串S由s循环k次构成,那么有S[0-->L-len-1] == S[len-->L-1],即前k-1个循环节和后k-1个循环节构成的字符串相等 那么此时KMP数组的next[L] = k-1个循环节的长度, 也即 nex

【转载】字符串最小表示法-O(n)算法

原博客链接:http://blog.csdn.net/zy691357966/article/details/39854359 未授权,侵权删. 因为这篇博客写得真好..转载了.. 红色的字是原博主写的,蓝色的字是我加的. ------------------------------------------------------------------------------------------------------------------------------------------