HDU 3374 String Problem (KMP+最大最小表示法)
2015-11-23 00:48
316 查看
题意:给出一个字符串s,如 abcdef,可以对字符串进行左移操作,于是就可以生成如下strlen个串:abcdef,bcdefa,cdefab,defabc……求这些串中 字典序小和最大的串 开头的字母在原串中的位置,如果有多个输出序号最小的位置,并且输出这样的串总共有多少个。
输出格式:posmin countmin posmax countmax
收获:
1.用kmp找循环节。
(仔细分析,我们会发现countmin=countmax。因为向左移动后如果出现相同的串,说明这是一个循环,接着第二个出现的相同的串移动会有相同的结果。)
所谓循环节就是:abcabcabc 循环节就是3。
首先我们需要知道这个串有没有循环节:
我们知道nxt
表示的是以最后一个字符结尾的最长前缀的长度(kmp可知)。如果这个串有循环节,则s[0~(len-nxt
-1)]必定为这个串的循环节(即上例中的abc,可以任意举含循环节的例子验证),则 len % (len-nxt
)==0,反过来推亦成立,所以这个为 s串有循环节的充要条件。
根据上诉所说,我们就可以知道循环节的长度为(len-nxt
)。
既然知道了长度,那么重复出现的个数=len/(len-nxt[n])。
2.找到字典序最大and最小的串。
以找最小为例。
朴素想法是通过两个指针进行 O(n^2) 枚举:
i 为枚举串的串头,j表示比较串的串头。
初始:i=0;j=1;
分3种情况:
==s[i]< s[j] j++;
==s[i]> s[j] i=j;j=j+1;
==s[i]==s[j] 就利用k 来接着i,j向后面比较 s[i+k] s[j+k].
====s[i+k]==s[j+k] k++
====s[i+k]< s[j+k] j++ ;k=0
====s[i+k]> s[j+k] i=j ;j=j+1; k=0
最后答案为 i;
对于 bbbbbbba 这样的串呢,我们可以轻松知道它的复杂度为n^2;
于是我们需要优化,而i+1~j-1其实都是比s[i]大的元素了。
==s[i]< s[j] j++;
==s[i]> s[j] i=j;j=j+1;
==s[i]==s[j] 就利用k 来接着i,j向后面比较 s[i+k] s[j+k].
====s[i+k]==s[j+k] k++
====s[i+k]< s[j+k] j=j+k+1 ;k=0
====s[i+k]> s[j+k] i=i+k+1;;k=0
只有加粗这一行不同。i,j位置的生成其实本身就继承了一些东西,所以可以直接转移到i+K+1。
因为i+k总共只会吧n扫一遍,j+k总共只会把n扫一遍 所以并起来(并不是相加) 复杂度就是O(N)的。
3.kmp姿势问题
我一直以来写getnxt的姿势都是
其实本身没错。。。只是不能算出nxt[n]。。。。��
所以从今以后我要换一种比较普遍而简洁的写法。
因为循环节的原因,这题又一次加深了我对kmp的nxt数组理解。
4.
字符串函数 strcat()又把我坑了。。。一开始我的打算是直接把原串扩为两倍。。。这样方便一点,这样T。。于是我又对它的复杂度产生了怀疑,并且没有搜到。
最后还是只能mod 来处理。
输出格式:posmin countmin posmax countmax
收获:
1.用kmp找循环节。
(仔细分析,我们会发现countmin=countmax。因为向左移动后如果出现相同的串,说明这是一个循环,接着第二个出现的相同的串移动会有相同的结果。)
所谓循环节就是:abcabcabc 循环节就是3。
首先我们需要知道这个串有没有循环节:
我们知道nxt
表示的是以最后一个字符结尾的最长前缀的长度(kmp可知)。如果这个串有循环节,则s[0~(len-nxt
-1)]必定为这个串的循环节(即上例中的abc,可以任意举含循环节的例子验证),则 len % (len-nxt
)==0,反过来推亦成立,所以这个为 s串有循环节的充要条件。
根据上诉所说,我们就可以知道循环节的长度为(len-nxt
)。
既然知道了长度,那么重复出现的个数=len/(len-nxt[n])。
2.找到字典序最大and最小的串。
以找最小为例。
朴素想法是通过两个指针进行 O(n^2) 枚举:
i 为枚举串的串头,j表示比较串的串头。
初始:i=0;j=1;
分3种情况:
==s[i]< s[j] j++;
==s[i]> s[j] i=j;j=j+1;
==s[i]==s[j] 就利用k 来接着i,j向后面比较 s[i+k] s[j+k].
====s[i+k]==s[j+k] k++
====s[i+k]< s[j+k] j++ ;k=0
====s[i+k]> s[j+k] i=j ;j=j+1; k=0
最后答案为 i;
对于 bbbbbbba 这样的串呢,我们可以轻松知道它的复杂度为n^2;
于是我们需要优化,而i+1~j-1其实都是比s[i]大的元素了。
==s[i]< s[j] j++;
==s[i]> s[j] i=j;j=j+1;
==s[i]==s[j] 就利用k 来接着i,j向后面比较 s[i+k] s[j+k].
====s[i+k]==s[j+k] k++
====s[i+k]< s[j+k] j=j+k+1 ;k=0
====s[i+k]> s[j+k] i=i+k+1;;k=0
只有加粗这一行不同。i,j位置的生成其实本身就继承了一些东西,所以可以直接转移到i+K+1。
因为i+k总共只会吧n扫一遍,j+k总共只会把n扫一遍 所以并起来(并不是相加) 复杂度就是O(N)的。
3.kmp姿势问题
我一直以来写getnxt的姿势都是
nxt[0]=-1;nxt[1]=0;int p; for(int i=2;i<n;i++) { p=i-1; while(p>0 && sz[nxt[p]]!=sz[i-1]) p=nxt[p]; nxt[i]=nxt[p]+1; }
其实本身没错。。。只是不能算出nxt[n]。。。。��
所以从今以后我要换一种比较普遍而简洁的写法。
因为循环节的原因,这题又一次加深了我对kmp的nxt数组理解。
4.
字符串函数 strcat()又把我坑了。。。一开始我的打算是直接把原串扩为两倍。。。这样方便一点,这样T。。于是我又对它的复杂度产生了怀疑,并且没有搜到。
最后还是只能mod 来处理。
// Created by ZYD in 2015. // Copyright (c) 2015 ZYD. All rights reserved. // #include <cstdio> #include <cstdlib> #include <iostream> #include <algorithm> #include <cstring> #include <climits> #include <string> #include <vector> #include <cmath> #include <stack> #include <queue> #include <set> #include <map> using namespace std; #define Size 100000 #define ll long long #define mk make_pair #define pb push_back #define mem(array) memset(array,0,sizeof(array)) typedef pair<int,int> P; int nxt[2000005],n; char sz[1000005]; int kmp() { int i,j,p; nxt[0]=-1; nxt[1]=0; for(int i=2;i<n;i++) { p=i-1; while(p>0 && sz[nxt[p]]!=sz[i-1]) p=nxt[p]; nxt[i]=nxt[p]+1; } return 0; } int getmin() { int i,j,k,tmp; i=0;j=1;k=0; // strcat(sz,sz); while(i<n && j<n && k<n) { tmp=sz[(i+k)%n]-sz[(j+k)%n]; if(tmp==0) { k++; } else { if(tmp>0) { i=i+k+1; } else j=j+k+1; if(i==j) j++; k=0; } } return min(i,j); } int getmax() { int i,j,k,tmp; i=0;j=1;k=0; while(i<n && j<n && k<n) { tmp=sz[(i+k)%n]-sz[(j+k)%n]; if(tmp==0) { k++; } else { if(tmp<0) { i=i+k+1; } else j=j+k+1; if(i==j) j++; k=0; } } return min(i,j); } int main() { freopen("in.txt","r",stdin); while(~scanf("%s",sz)) { n=strlen(sz); mem(nxt); kmp(); int r=n-nxt ; if(n % r==0) r=n/r; else r=1; // for(int i=0;i<n;i++) cout<<sz[i]<<" ";cout<<endl; // for(int i=0;i<n;i++) cout<<nxt[i]<<" ";cout<<endl; printf("%d %d %d %d\n",getmin()+1,r,getmax()+1,r); } return 0; }
相关文章推荐
- 正则表达式 效率
- 设计模式小结
- 开源项目托管GitHub
- 如何设置一个严格30分钟过期的Session
- 二叉树首尾点的寻找
- STUN和TURN技术浅析
- 正则表达式高级2.1
- DOM
- css
- web
- ion-content中存在ion-list,在ion-list外固定其他控件位置不滚动
- ubuntu安装php-cgi
- 【经典算法】:二分查找
- responsiveslides 插件(图片轮播插件)
- css3实现逐渐变大的圆填充div背景的效果
- 学习Python (三)
- 正则表达式高级
- PXE+kickstart实现批量部署linux系统
- POJ3664---没使用结构体,用数组(未AC)
- Android开发环境搭建