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

设字符串长度为\(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\)。

最新文章

  1. how-to-add-global-asp-net-web-api-filters
  2. 优化加载jQuery的方法
  3. 得到UIView中某个非子视图在UIView中的位置
  4. Windows 7远程桌面连接Ubuntu 16.04
  5. Python Django manage.py提供的命令及用法
  6. Linux sed命令常用方法
  7. PHP移动互联网开发(1)——环境搭建及配置
  8. AS--&gt;如何更高效的使用 Gradle, 快速build apk
  9. 搭建DNS服务
  10. Winform跨窗体操作控件(使用委托)
  11. 【转】Matlab中的括号()[] {}
  12. Hibernate注解开发详解
  13. Solr(六)Solr索引数据存放到HDFS下
  14. 用MyEclipse自带工具生成WebService客户端代码
  15. spring mvc 自动生成代码
  16. 添加dubbo.xsd的方法
  17. pytorch官网上两个例程
  18. Go语言之高级篇Beego框架之爬虫项目实战
  19. 基于无锁队列和c++11的高性能线程池
  20. [android] 新闻客户端实现左侧导航点击切换

热门文章

  1. grouped differently across partitions
  2. request,session,application三者关系&lt;转&gt;
  3. access 连接数据库
  4. 云计算系列——HIVE1.2.1 环境搭建
  5. Microsoft.AspNetCore.Identity 使用 mysql 报错处理
  6. Codeforces Round #340 (Div. 2) E. XOR and Favorite Number —— 莫队算法
  7. BZOJ 1632 [Usaco2007 Feb]Lilypad Pond:spfa【同时更新:经过边的数量最小】【路径数量】
  8. 通过 :hover 伪元素控制其他元素
  9. Python:循环
  10. OC-内存管理的所有权链问题