字符串处理其实也是OI中的一个重点,各种算法层出不穷,具体可以看我滴字符串算法那篇。当然我们这入门真是好难啊!不过在经历一系列修改后,这天的题全A了!然而……最后一题是学了字典树才会做啦~
====================CUT LINE====================
A -POJ3461
ime Limit:1000MS Memory Limit:65536KB
Description
The French author Georges Perec (1936–1982) once wrote a book, La disparition, without the letter ‘e’. He was a member of the Oulipo group. A quote from the book:
Tout avait Pair normal, mais tout s’affirmait faux. Tout avait Fair normal, d’abord, puis surgissait l’inhumain, l’affolant. Il aurait voulu savoir où s’articulait l’association qui l’unissait au roman : stir son tapis, assaillant à tout instant son imagination, l’intuition d’un tabou, la vision d’un mal obscur, d’un quoi vacant, d’un non-dit : la vision, l’avision d’un oubli commandant tout, où s’abolissait la raison : tout avait l’air normal mais…
Perec would probably have scored high (or rather, low) in the following contest. People are asked to write a perhaps even meaningful text on some subject with as few occurrences of a given “word” as possible. Our task is to provide the jury with a program that counts these occurrences, in order to obtain a ranking of the competitors. These competitors often write very long texts with nonsense meaning; a sequence of 500,000 consecutive ‘T’s is not unusual. And they never use spaces.
So we want to quickly find out how often a word, i.e., a given string, occurs in a text. More formally: given the alphabet {‘A’, ‘B’, ‘C’, …, ‘Z’} and two finite strings over that alphabet, a word W and a text T, count the number of occurrences of W in T. All the consecutive characters of W must exactly match consecutive characters of T. Occurrences may overlap.
Input
The first line of the input file contains a single number: the number of test cases to follow. Each test case has the following format:
One line with the word W, a string over {'A', 'B', 'C', …, 'Z'}, with 1 ≤ |W| ≤ 10,000 (here |W| denotes the length of the string W).
One line with the text T, a string over {'A', 'B', 'C', …, 'Z'}, with |W| ≤ |T| ≤ 1,000,000.
Output
For every test case in the input file, the output should contain a single number, on a single line: the number of occurrences of the word W in the text T.
Sample Input
3
BAPC
BAPC
AZA
AZAZAZA
VERDI
AVERDXIVYERDIAN
Sample Output
1
3
0
【题意分析】
在一个字符串S里面找另一个字符串T出现的次数……模板题不解释。
【解题思路】
KMP算法。
【AC代码】
#include
#include
#include
using namespace std;
int n,k,lenW,lenT,ans;
char W[10002],T[1000002];
int next[10002];
void readin()
{
scanf("%s",W);
scanf("%s",T);
lenW=strlen(W);
lenT=strlen(T);
}
void KMP()
{
next[0]=next[1]=0;k=0;
for(int i=1;iwhile(k && W[i]!=W[k])
k=next[k];
if(W[i]==W[k])
next[i+1]=++k;
else
next[i+1]=0;
}
k=ans=0;
for(int i=0;iwhile(k && T[i]!=W[k])
k=next[k];
if(T[i]==W[k])
++k;
if(k==lenW)
{
++ans;
k=next[k];
}
}
printf("%d\n",ans);
}
int main()
{
scanf("%d",&n);
for(int i=0;ireturn 0;
}
B - HDU3068
Time Limit:2000MS Memory Limit:32768KB
Description
给出一个只由小写英文字符a,b,c…y,z组成的字符串S,求S中最长回文串的长度.
回文就是正反读都是一样的字符串,如aba, abba等
Input
输入有多组case,不超过120组,每组输入为一行小写英文字符a,b,c…y,z组成的字符串S
两组case之间由空行隔开(该空行不用处理)
字符串长度len <= 110000
Output
每一行一个整数x,对应一组case,表示该组case的字符串中所包含的最长回文长度.
Sample Input
aaaa
abab
Sample Output
4
3
【题目分析】
不用分析。
【解题思路】
manacher。
#include
#include
#include
#include
using namespace std;
char S[500000];
int ans[500000];
int len,p,realans;
void change()
{
len=strlen(S);
// printf("len:%d\n",len);
for(int i=len-1;i>=0;--i)
{
S[i*2+2]=S[i];
S[i*2+1]='#';
}
S[len*2+1]='#';
S[0]='@';S[len*2+2]='$';
// printf("len:%d\n",len);
len=len*2+1;
// printf("len:%d\n",len);
len--;
// printf("len:%d\n",len);
//printf("%s %d",S,len);
}
void manacher()
{
// printf("len:%d\n",len);
memset(ans,0,sizeof(ans));
ans[0]=ans[1]=0;p=1;
for(int i=2;i<=len;++i)
{
ans[i]=max(0, min(ans[2*p-i],p+ans[p]-i) );
while (S[i-ans[i]]==S[i+ans[i]])
ans[i]++;
if(i+ans[i]>p+ans[p])
p=i;
}
/* printf("len:%d\n",len);
for(int i=0;i<=len;++i)
printf("%d ",ans[i]);
printf("\n");*/
realans=0;
for(int i=2;i<=len;++i)
realans=max(ans[i],realans);
printf("%d\n",realans-1);
S[0]='\0';
// memset(S,'/0',sizeof(S));
}
int main()
{
while(~scanf("%s",S))
{
change();
// printf("len:%d\n",len);
manacher();
}
return 0;
}
C - POJ2185
Time Limit:3000MS Memory Limit:65536KB
Description
Every morning when they are milked, the Farmer John’s cows form a rectangular grid that is R (1 <= R <= 10,000) rows by C (1 <= C <= 75) columns. As we all know, Farmer John is quite the expert on cow behavior, and is currently writing a book about feeding behavior in cows. He notices that if each cow is labeled with an uppercase letter indicating its breed, the two-dimensional pattern formed by his cows during milking sometimes seems to be made from smaller repeating rectangular patterns.
Help FJ find the rectangular unit of smallest area that can be repetitively tiled to make up the entire milking grid. Note that the dimensions of the small rectangular unit do not necessarily need to divide evenly the dimensions of the entire milking grid, as indicated in the sample input below.
Input
* Line 1: Two space-separated integers: R and C
Output
* Line 1: The area of the smallest unit from which the grid is formed
Sample Input
2 5
ABABA
ABABA
Sample Output
2
Hint
The entire milking grid can be constructed from repetitions of the pattern ‘AB’.
【题目分析】
现在给出一个字符矩阵,只有26个大写字母,现在要求它的一个子矩阵,使这个子矩阵能在重复多次后铺满整个原矩阵。铺的范围可以超出原矩阵,但不能翻转、重叠。输出子矩阵面积。
【解题思路】
这题初看是有思路,即每一行做KMP,求行的最小公倍数,然后再每一列做KMP,求列的最小公倍数,乘起来。然而发现是有特例的:
2 8
ABCDEFAB
AAAABAAA
这样做就显然是错了。
经过分析后,我们发现,是因为有些会算重复,所以正确的做法应该是:
找出每行的重复子串长度的各种可能情况,然后每行都有的并且是最小长度作为宽width。
第二步找最小重复子矩阵的高,取每行的宽为width的前缀作为一个单位,对这0到r-1个单位求出KMP的next函数,找出最小重复子序列的单位数作为高height,最终答案为width*height。
然后这样得出来的就是正解啦~
【AC代码】
#include
#include
#include
using namespace std;
char st[10002][80];
int n,m,ans,tmp;
int p[10002],lc1[10002],lc2[80];
int gcd(int a,int b)
{
return b==0? a:gcd(b,a%b);
}
int lcm(int a,int b)
{
return a*b/gcd(a,b);
}
void readin()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
{
getchar();
for(int j=1;j<=m;++j)
st[i][j]=getchar();
}
}
void solve()
{
for(int i=1;i<=n;++i)
{
p[1]=0;
int k=0;
for(int j=2;j<=m;j++)
{
while(k>0&&st[i][k+1]!=st[i][j])
k=p[k];
if(st[i][k+1]==st[i][j])
k++;
p[j]=k;
}
lc1[i]=m-p[m];
}
for(int i=1;i<=m;i++)
{
p[1]=0;
int k=0;
for(int j=2;j<=n;j++)
{
while(k>0&&st[k+1][i]!=st[j][i])
k=p[k];
if(st[k+1][i]==st[j][i])
k++;
p[j]=k;
}
lc2[i]=n-p[n];
}
tmp=ans=1;
for(int i=1;i<=n;++i)
tmp=lcm(tmp,lc1[i]);
printf("%d\n",tmp);
if(tmp>m)
tmp=m;
for(int i=1;i<=m;++i)
ans=lcm(ans,lc2[i]);
if(ans>n)
ans=n;
ans=tmp*ans;
printf("%d\n",ans);
}
int main()
{
readin();
solve();
return 0;
}
D - HDU4333
Time Limit:1000MS Memory Limit:32768KB
Description
One day Silence is interested in revolving the digits of a positive integer. In the revolving operation, he can put several last digits to the front of the integer. Of course, he can put all the digits to the front, so he will get the integer itself. For example, he can change 123 into 312, 231 and 123. Now he wanted to know how many different integers he can get that is less than the original integer, how many different integers he can get that is equal to the original integer and how many different integers he can get that is greater than the original integer. We will ensure that the original integer is positive and it has no leading zeros, but if we get an integer with some leading zeros by revolving the digits, we will regard the new integer as it has no leading zeros. For example, if the original integer is 104, we can get 410, 41 and 104.
Input
The first line of the input contains an integer T (1<=T<=50) which means the number of test cases.
For each test cases, there is only one line that is the original integer N. we will ensure that N is an positive integer without leading zeros and N is less than 10^100000.
Output
For each test case, please output a line which is “Case X: L E G”, X means the number of the test case. And L means the number of integers is less than N that we can get by revolving digits. E means the number of integers is equal to N. G means the number of integers is greater than N.
Sample Input
1
341
Sample Output
Case 1: 1 1 1
【题意分析】
这道题呢,意思很简单,就是说,有一个原串S,其长度为len(当然没有读入)。现在每一次把第一位移到最后一位,得到一个新的串,问在经过len次操作后,有多少个新串比原串小、一样、大。
【解题思路】
直接模拟比较显然是会超时, len2 级的。那么我们分析题目,显然,每次往后移动,比较的都是原串的一部分后缀加上一部分前缀,与原串比较。后缀与前缀的比较,很容易想到EXKMP(扩展KMP)。那么怎么做呢?显然,我们可以先把原串复制一次,再用扩展KMP比较每一个后缀,然后当比较到不同的时候,再比较下一位就行了。
时间复杂度是 O(len∗T)
【AC代码】
#include
#include
#include
#include
using namespace std;
int t,L,E,G,len,len2;
int NEXT[250000],ex[250000];
char S[250005];
void copy_one()
{
len=strlen(S);
for(int i=0;i*=2;
}
void get_NEXT()
{
NEXT[0]=len2;
int i=0,j,p,u;
while(S[i]==S[i+1]&&i+11]=i;
p=1;
for(i=2;iif(i+NEXT[i-p]else
{
j=max(NEXT[p]+p-i,0);
while(i+jfor(int i=0;iprintf("%d ",NEXT[i]);
printf("\n");*/
}
void EXKMP()
{
int i,j,p,u;
ex[0]=len2;p=0;
for(i=1;i//printf("%d %d\n",NEXT[i-p]+i,ex[p]+p);
if(i+NEXT[i-p]// printf("%d %d\n",i,p);
}
else
{
j=ex[p]+p-i;
if(j<0)j=0;
while(i+jfor(int i=0;iint tmp=ex[i];
if(tmp==len2)
++E;
else
if(S[i]else
++L;
}
/* for(int i=0;iprintf("%d ",ex[i]);
printf("\n");*/
}
int main()
{
scanf("%d",&t);
for(int CASE=1;CASE<=t;++CASE)
{
scanf("%s",S);
L=E=G=0;
copy_one();
get_NEXT();
EXKMP();
printf("Case %d: %d %d %d\n",CASE,L,E,G);
}
return 0;
}
E - HDU 3613
Time Limit:1000MS Memory Limit:65536KB
Description
After an uphill battle, General Li won a great victory. Now the head of state decide to reward him with honor and treasures for his great exploit.
One of these treasures is a necklace made up of 26 different kinds of gemstones, and the length of the necklace is n. (That is to say: n gemstones are stringed together to constitute this necklace, and each of these gemstones belongs to only one of the 26 kinds.)
In accordance with the classical view, a necklace is valuable if and only if it is a palindrome - the necklace looks the same in either direction. However, the necklace we mentioned above may not a palindrome at the beginning. So the head of state decide to cut the necklace into two part, and then give both of them to General Li.
All gemstones of the same kind has the same value (may be positive or negative because of their quality - some kinds are beautiful while some others may looks just like normal stones). A necklace that is palindrom has value equal to the sum of its gemstones’ value. while a necklace that is not palindrom has value zero.
Now the problem is: how to cut the given necklace so that the sum of the two necklaces’s value is greatest. Output this value.
Input
The first line of input is a single integer T (1 ≤ T ≤ 10) - the number of test cases. The description of these test cases follows.
For each test case, the first line is 26 integers: v 1, v 2, …, v 26 (-100 ≤ v i ≤ 100, 1 ≤ i ≤ 26), represent the value of gemstones of each kind.
The second line of each test case is a string made up of charactor ‘a’ to ‘z’. representing the necklace. Different charactor representing different kinds of gemstones, and the value of ‘a’ is v 1, the value of ‘b’ is v 2, …, and so on. The length of the string is no more than 500000.
Output
Output a single Integer: the maximum value General Li can get from the necklace.
Sample Input
2
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
aba
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
acacac
Sample Output
1
6
【题目大意】
首先有多组数据……每组数据先有26个数字,表示 ′a′..′z′ 的字母价值。现在再给出一个字符串,要求把它分成前后两部分。若某部分是回文串,则它的价值要加上这个回文串每一位字母的价值。
【解题思路】
回文串,我果断想到manacher,然后怎么做呢,先看看暴力:直接枚举切分点,然后就知道前后是不是回文串,然后再用个前缀和记录前i个的价值,显然是 O(len) 的。
【AC代码】
#include
#include
#include
#include
using namespace std;
char S[1000000];
int ans[1000000],sum[1000000],val[28];
bool per[1000000],suf[1000000];
int len,len2,p,realans,t,temp;
void change()
{
len=strlen(S);
memset(sum,0,sizeof(sum));
for(int i=1;i<=len;++i)
sum[i]=sum[i-1]+val[S[i-1]-'a'];
for(int i=len-1;i>=0;--i)
{
S[i*2+2]=S[i];
S[i*2+1]='#';
}
S[len*2+1]='#';
S[0]='@';S[len*2+2]='$';
len2=len;
len=len*2+1;
len--;
}
void manacher()
{
memset(per,false,sizeof(per));
memset(suf,false,sizeof(suf));
memset(ans,0,sizeof(ans));
ans[0]=ans[1]=0;p=1;
for(int i=2;i<=len;++i)
{
if(ans[p]+p>i)
ans[i]=min(ans[2*p-i],p+ans[p]-i);
else
ans[i]=1;
//ans[i]=max(0, min(ans[2*p-i],p+ans[p]-i) );
while (S[i-ans[i]]==S[i+ans[i]])
ans[i]++;
if(i+ans[i]>p+ans[p])
p=i;
if(i-ans[i]==0)
per[ans[i]-1]=true;
if(i+ans[i]==len+2)
suf[ans[i]-1]=true;
}
realans=0;
for(int i=1;iif(per[i])
temp+=sum[i];
if(suf[len2-i])
temp+=sum[len2]-sum[i];
if(temp>realans)
realans=temp;
temp=0;
}
printf("%d\n",realans);
S[0]='\0';
}
int main()
{
scanf("%d",&t);
while(t--)
{
for(int i=0;i<26;++i)
scanf("%d",&val[i]);
scanf("%s",S);
change();
manacher();
}
return 0;
}
F - HDU3746
Time Limit:1000MS Memory Limit:32768KB
Description
CC always becomes very depressed at the end of this month, he has checked his credit card yesterday, without any surprise, there are only 99.9 yuan left. he is too distressed and thinking about how to tide over the last days. Being inspired by the entrepreneurial spirit of “HDU CakeMan”, he wants to sell some little things to make money. Of course, this is not an easy task.
As Christmas is around the corner, Boys are busy in choosing christmas presents to send to their girlfriends. It is believed that chain bracelet is a good choice. However, Things are not always so simple, as is known to everyone, girl’s fond of the colorful decoration to make bracelet appears vivid and lively, meanwhile they want to display their mature side as college students. after CC understands the girls demands, he intends to sell the chain bracelet called CharmBracelet. The CharmBracelet is made up with colorful pearls to show girls’ lively, and the most important thing is that it must be connected by a cyclic chain which means the color of pearls are cyclic connected from the left to right. And the cyclic count must be more than one. If you connect the leftmost pearl and the rightmost pearl of such chain, you can make a CharmBracelet. Just like the pictrue below, this CharmBracelet’s cycle is 9 and its cyclic count is 2:
Now CC has brought in some ordinary bracelet chains, he wants to buy minimum number of pearls to make CharmBracelets so that he can save more money. but when remaking the bracelet, he can only add color pearls to the left end and right end of the chain, that is to say, adding to the middle is forbidden.
CC is satisfied with his ideas and ask you for help.
Input
The first line of the input is a single integer T ( 0 < T <= 100 ) which means the number of test cases.
Each test case contains only one line describe the original ordinary chain to be remade. Each character in the string stands for one pearl and there are 26 kinds of pearls being described by ‘a’ ~’z’ characters. The length of the string Len: ( 3 <= Len <= 100000 ).
Output
For each case, you are required to output the minimum count of pearls added to make a CharmBracelet.
Sample Input
3
aaa
abca
abcde
Sample Output
0
2
5
【题目分析】
首先还是多组数据咯,然后就是有个字符串,问要在这个字符串后面加多少个字符,才能让这个字符串形成一个循环字符串。
【解题思路】
循环,让人不禁想到了KMP,但KMP的算法好像也不完全是。但NEXT数组的性质和循环的性质类似,所以我们就从这个地方想起,发现可以通过mod来得到循环节长度。其实这题和之前的一题差不多,没错就是C。
【AC代码】
#include
#include
#include
#include
using namespace std;
#define N 100010
char s[N];
int nextval[N];
int len;
void getnext(const char *s)
{
int i = 0, j = -1;
nextval[0] = -1;
while(i != len)
{
if(j == -1 || s[i] == s[j])
nextval[++i] = ++j;
else
j = nextval[j];
}
}
int main()
{
int ncase;
int length, add;
scanf("%d", &ncase);
while(ncase--)
{
scanf("%s", s);
len = strlen(s);
getnext(s);
length = len - nextval[len]; //循环节的长度
if(len != length && len % length == 0) //循环多次
printf("0\n");
else
{
add = length - nextval[len] % length; //取余的作用:abcab,去掉abc
printf("%d\n",add);
}
}
return 0;
}
G - POJ3376
Time Limit:10000MS Memory Limit:262144KB
Description
A word is called a palindrome if we read from right to left is as same as we read from left to right. For example, “dad”, “eye” and “racecar” are all palindromes, but “odd”, “see” and “orange” are not palindromes.
Given n strings, you can generate n × n pairs of them and concatenate the pairs into single words. The task is to count how many of the so generated words are palindromes.
Input
The first line of input file contains the number of strings n. The following n lines describe each string:
The i+1-th line contains the length of the i-th string li, then a single space and a string of li small letters of English alphabet.
You can assume that the total length of all strings will not exceed 2,000,000. Two strings in different line may be the same.
Output
Print out only one integer, the number of palindromes.
Sample Input
3
1 a
2 ab
2 ba
Sample Output
5
Hint
The 5 palindromes are:
a a a ba ab a ab ba ba ab
【题目大意】
就是有n个字符串,每两个字符串首尾相连,有n*n种方法。比如n=2,两个字符串是a和b,那么就可以变成:aa,ab,ba,bb,就是说自己和自己相连只算一种。
那么求相连后能是回文串的种数。
【解题思路】
其实,我用字符串是不会做的,但在学了Trie后就只会用字典树做啦~就是先用manacher求出每个字符串在每个位置的回文长度,然后把每个字符串放进字典树,再把每个字符串倒序去匹配,此时会出现以下两种情况:
我们首先假设一个字符串是S,另一个是T,那么若是S的长度小于T的长度,则,T的倒序前缀与S完全匹配,若T的剩下一部分是回文串,则这种组合是可以的。
反之,若S的长度大于T的长度,且S比T多出的一部分是回文串,则也是一种可行的组合。
那么接下来,考虑在字典树上要记录的东西。显然,一个是有多少字符串在此结束,第二是在这个点之后有多少个回文串,这样就能解决这题。
哦不,因为直接用邻接矩阵去存储Trie会超空间,所以我们还是使用邻接表或链式前向星。
【AC代码】
#include
#include
#include
#include
#include
using namespace std;
const int MAXL =2000010;
long long realans;
int l,n,sz;
char s[MAXL],s2[MAXL*3];
int begin[MAXL],ans[MAXL*3];//string end here/string will be circle after here
int allans[MAXL*3];
struct TrieNode
{
int num,cnt;//endst,lastcirclest
TrieNode*nxt[26];
TrieNode(){
memset(nxt,0,sizeof(nxt));
}
};
TrieNode node[MAXL];
TrieNode* root;
int tot;
void readin()
{
scanf("%d",&n);
begin[0]=0;
for(int i=0;iscanf("%d%s",&l,s);
s2[begin[i]]='#';
for(int j=0;j2+begin[i]+1]=s[j];
s2[j*2+begin[i]+2]='#';
}
begin[i+1]=begin[i]+l*2+1;
}
}
void _reverse(int ps)
{
int mid=(begin[ps+1]+begin[ps])/2;
for(int i=begin[ps],j=begin[ps+1]-1;iint Insert(int ps)
{
TrieNode* tmpk=root;
int u=0,k=begin[ps];
int len=(begin[ps+1]-begin[ps])/2;
for(int i=0;iint np=k+i*2+1;
int p=s2[np]-'a';
int tmp=i*2+2;//now letter pos
int tmp2=len*2+1;//all len
if(tmpk->nxt[p]==NULL)
tmpk->nxt[p]=&node[tot++];
tmpk=tmpk->nxt[p];
if(tmp2-tmp==allans[k-1+(tmp2-tmp+1)/2+tmp] && i!=len-1)
{
tmpk->cnt++;//wrong
//printf("%d\n",ps);
}
}
tmpk->num++;
}
int Query(int ps)
{
TrieNode* tmpk=root;
int u=0,k=begin[ps];
int len=(begin[ps+1]-begin[ps])/2;
int tmpans=0;
for(int i=0;iint np=k+i*2+1;
int p=s2[np]-'a';
tmpk=tmpk->nxt[p];
if (tmpk==NULL)
return tmpans;
if(tmpk->num)
{
int tmp=i*2+2;//now letter pos
int tmp2=len*2+1;//all len
if(tmp2-tmp==allans[k-1+(tmp2+tmp+1)/2])
{
tmpans+=tmpk->num;
//printf("%d\n",tmpk->num);
}
}
}
tmpans+=tmpk->cnt;
//printf("cnt:%d\n",tmpk->cnt);
return tmpans;
}
void manacher(int ps)
{
int len=begin[ps+1]-begin[ps];
int k=begin[ps];
ans[0]=ans[1]=1;
int p=1;
for(int i=1;i<=len;++i)
{
ans[i]=max(0, min(ans[2*p-i],p+ans[p]-i) );
while (s2[i-ans[i]+k]==s2[i+ans[i]+k] &&i+ans[i]1])
ans[i]++;
if(i+ans[i]>p+ans[p])
p=i;
}
for(int i=0;i2-1;
Insert(ps);
}
void debug()
{
for(int i=0;iprintf("%c ",s2[i]);
printf("\n");
for(int i=0;iprintf("%d ",allans[i]);
printf("\n");
}
int main()
{
root = new TrieNode();
readin();
// debug();
for(int i=0;i// debug();
for(int i=0;i// debug();
printf("%lld\n",realans);
return 0;
}
啊,字符串主要是要转化问题,boom