(2019.9.5~2019.9.11)补题汇总(字符串相关)
[1] [HDU3374]String Problem
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 5489 Accepted Submission(s): 2258
Problem Description
Give you a string with length N, you can generate N strings by left shifts. For example let consider the string “SKYLONG”, we can generate seven strings:
String Rank
SKYLONG 1
KYLONGS 2
YLONGSK 3
LONGSKY 4
ONGSKYL 5
NGSKYLO 6
GSKYLON 7
and lexicographically first of them is GSKYLON, lexicographically last is YLONGSK, both of them appear only once.
Your task is easy, calculate the lexicographically fisrt string’s Rank (if there are multiple answers, choose the smallest one), its times, lexicographically last string’s Rank (if there are multiple answers, choose the smallest one), and its times also.
Input
Each line contains one line the string S with length N (N <= 1000000) formed by lower case letters.
Output
Output four integers separated by one space, lexicographically fisrt string’s Rank (if there are multiple answers, choose the smallest one), the string’s times in the N generated strings, lexicographically last string’s Rank (if there are multiple answers, choose the smallest one), and its times also.
Sample Input
abcder
aaaaaa
ababab
Sample Output
1 1 6 1
1 6 1 6
1 3 2 3
解题思路
挺综合的一道题(我还是太菜了2333)。需要求循环字符串中字典序最小和最大的并输出其出现的次数。求字符串用到最大最小表示法(图示更新中ing......),统计出现次数正好可以用到KMP中记录next(prefix)数组。刚开始用的是纯模拟优化了一下直接wa了。。。。。。后来不读一下全是班子,emm......
代码样例(转自ChenJr)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2000005;
char s[maxn];
int nextt[maxn];
void get_next(char *a, int len)
{ //求解next数组板子
int i = 0, j = nextt[0] = -1;
while (i < len)
{
while (j != -1 && a[i] != a[j])
j = nextt[j];
nextt[++i] = ++j;
}
}
int get_minn(char *a, int len)
{ //最小表示法板子
int i = 0, j = 1;
for (int i = 0; i < len; i++)
{
s[i + len] = s[i];
}
while (i < len && j < len)
{
int k;
for (k = 0; k < len && a[k + i] == a[k + j]; k++)
;
if (k == len)
break;
if (a[k + i] > a[k + j])
{
i = i + k + 1;
if (i == j)
i++;
}
else
{
j = j + k + 1;
if (i == j)
j++;
}
}
return min(i, j);
}
int get_maxx(char *a, int len)
{ //最大表示法板子
int i = 0, j = 1;
for (int i = 0; i < len; i++)
{
s[i + len] = s[i];
}
while (i < len && j < len)
{
int k;
for (k = 0; k < len && a[k + i] == a[k + j]; k++)
;
if (k == len)
break;
if (a[k + i] < a[k + j])
{
i = i + k + 1;
if (i == j)
i++;
}
else
{
j = j + k + 1;
if (i == j)
j++;
}
}
return min(i, j);
}
int main()
{
while (~scanf("%s", s))
{
int len = strlen(s);
memset(nextt, 0, sizeof(nextt));
get_next(s, len);
int minn = get_minn(s, len);
int maxx = get_maxx(s, len);
int tmp = nextt[len];
if (len % (len - tmp) != 0)
{
cout << minn + 1 << " 1 " << maxx + 1 << " 1" << endl;
}
else
{
cout << minn + 1 << " " << len / (len - tmp) << " " << maxx + 1 << " " << len / (len - tmp) << endl;
}
}
return 0;
}
[2] [HDU-6229]string matching
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)Total Submission(s): 1708 Accepted Submission(s): 706
Problem Description
String matching is a common type of problem in computer science. One string matching problem is as following:
Given a string s[0…len−1], please calculate the length of the longest common prefix of s[i…len−1] and s[0…len−1] for each i>0.
I believe everyone can do it by brute force.
The pseudo code of the brute force approach is as the following:
We are wondering, for any given string, what is the number of compare operations invoked if we use the above algorithm. Please tell us the answer before we attempt to run this algorithm.
Input
The first line contains an integer T, denoting the number of test cases.
Each test case contains one string in a line consisting of printable ASCII characters except space.
* 1≤T≤30
* string length ≤106 for every string
Output
For each test, print an integer in one line indicating the number of compare operations invoked if we run the algorithm in the statement against the input string.
Sample Input
3
_Happy_New_Year_
ywwyww
zjczzzjczjczzzjc
Sample Output
17
7
32
解题思路
扩展KMP,一开始以为是ac自动机,结果gg。利用扩展KMP求出每一位的最长字符子串,如果这一位的位置加上它的子串长没用超过字符串的最后一位,在统计时还要加上一次比较次数。
*扩展KMP参考《拓展kmp算法总结》
代码样例
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int K=1000005;
int nt[K],extand[K];
char S[K],T[K];
int a[K];
ll ans;
void Getnext(char *T,int *next)
{
int len=strlen(T),a=0;
next[0]=len;
while(a<len-1 && T[a]==T[a+1]) a++;
next[1]=a;
a=1;
for(int k=2; k<len; k++)
{
int p=a+next[a]-1,L=next[k-a];
if( (k-1)+L >= p)
{
int j = (p-k+1)>0 ? (p-k+1) : 0;
while(k+j<len && T[k+j]==T[j]) j++;
next[k]=j;
a=k;
}
else
next[k]=L;
}
}
void GetExtand(char *S,char *T,int *next)
{
Getnext(T,next);
int slen=strlen(S),tlen=strlen(T),a=0;
int MinLen = slen < tlen ? slen : tlen;
while(a<MinLen && S[a]==T[a]) a++;
extand[0]=a;
a=0;
for(int k=1; k<slen; k++)
{
int p=a+extand[a]-1, L=next[k-a];
if( (k-1)+L >= p)
{
int j= (p-k+1) > 0 ? (p-k+1) : 0;
while(k+j<slen && j<tlen && S[k+j]==T[j]) j++;
extand[k]=j;
a=k;
}
else
extand[k]=L;
}
}
int main(void)
{
int tl;scanf("%lld",&tl);
while(tl--)
{
memset(nt,0,sizeof(nt));
memset(extand,0,sizeof(extand));
memset(a,0,sizeof(a));
scanf("%s",S);
ans = 0;
strcpy(T,S);
GetExtand(S,T,nt);
int len = strlen(T);
queue<int> id;
ans = 0;
ans+=len-1;
for(int i=1; i<len; i++) {
if(nt[i]!=0) {
ans+=nt[i];
if(i+nt[i]>=len)ans--;
}
}
printf("%lld\n",ans);
}
return 0;
}