week5小结(字符串类)

碰巧今天是NOIP(CSP)复赛日,学弟学妹们冲鸭!rp++!

不存在的分割线

1.字符串数据常用函数

关于char:

复制 strcpy



int main()
{
    char src[]="abcde";
    char dest[100];
    strcpy(dest,src);
    printf("%s",dest);
    //输出>> abcde 
}

部分复制strncpy



int main()
{
    char src[]="SWT is so great! We should % him everyday.";
    char dest[100];

    memset(dest,0,sizeof(dest));
    strncpy(dest,src,10);
    printf("%s\n",dest);
    //输出>>SWT is so 

    memset(dest,0,sizeof(dest));
    strncpy(dest,src,sizeof(src));
    printf("%s\n",dest);
    //输出>>SWT is so great! We should % him everyday.

    memset(dest,0,sizeof(dest));
    strncpy(dest,src,sizeof(dest));
    printf("%s\n",dest);
    //输出>>SWT is so great! We should % him everyday.

    char des[10];
    memset(des,0,sizeof(des));
    strncpy(des,src,sizeof(src));
    printf("%s\n",des);
    //exe停止工作 
}

合并strcat
把src所指字符串添加到dest结尾处(覆盖dest结尾处的 ‘\0’)并添加 ‘\0’


int main()
{
    char src[]="OI!";
    char dest[]="We like ";
    strcat(dest,src);
    printf("%s",dest);
    //输出>>We like OI! 
}

部分合并strncat


int main()
{
    char src[]="Please login in here!#$%@$@%@#$@%";
    char dest[]="Welcome to the largest talking room of SLYZ! ";
    strncat(dest,src,21);
    printf("%s",dest);
    //输出>>Welcome to the largest talking room of SLYZ! Please login in here! 
}

查找字符strchr



int main()
{
    char src[]="Can you find some thing?";
    int t=strchr(src,'?')-src;
    printf("%d",t);
    //输出>>23
} 

查找字符串strchr
char* strstr(char* haystack, char* needle);
从字符串haystack中寻找needle第一次出现的位置(不比较结束符”\0”)

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

using namespace std;

int main()
{
    char src[]="Can you find some thing?";
    int t=strstr(src,"thing")-src;
    printf("%d",t);
    //输出>>18
}

比较strcmp
比较字符串s1和s2,区分大小写
当s1 < s2时,返回值<0;
当s1 = s2时,返回值=0;
当s1 > s2时,返回值>0



int main()
{
    char src[]="Hello!";
    char dest[]="hello!";
    if (!strcmp(src,dest)) printf("SAME");
    else printf("DIFFERENT");
    //输出>>DIFFERENT 
}

*下面的函数并不常用 *

不区分字母的大小写的比较stricmp
比较字符串s1和s2,但不区分字母的大小写
当s1 < s2时,返回值<0;
当s1 = s2时,返回值=0;
当s1 > s2时,返回值>0

int main()
{
    char src[]="Hello!";
    char dest[]="hello!";
    if (!stricmp(src,dest)) printf("SAME");
    else printf("DIFFERENT");
    //输出>>SAME
}

部分比较且区分字母的大小写strncmp

int main()
{
    char src[]="A APPLE A DAY.";
    char dest[]="a apple a day.";
    if (strncmp(src,dest,8)==0) printf("SAME");
    else printf("DIFFERENT");
    //输出>>DIFFERENT 
} 

部分比较,不区分字母的大小写strnicmp

int main()
{
    char src[]="A APPLE A DAY.";
    char dest[]="a apple a day.";
    if (strnicmp(src,dest,8)==0) printf("SAME");
    else printf("DIFFERENT");
    //输出>>SAME 
}

转化之小写转大写strupr
只转换s中出现的小写字母,不改变其它字符
返回指向s的指针

int main()
{
    char s[]="Let's % SWT together!";
    strupr(s);
    printf("%s",s);
    //输出>>LET'S % SWT TOGETHER!
}

大写转小写 strlwr
将字符串s转换为小写形式
只转换s中出现的大写字母,不改变其它字符
返回指向s的指针

int main()
{
    char s[]="Let's % SWT together!";
    strlwr(s);
    printf("%s",s);
    //输出>>let's % swt together!
}

倒置strrev

int main()
{
    char s[]="!uoy kcor lliw eW";
    strrev(s);
    printf("%s",s);
    //输出>>We will rock you!
}

strset(设置)
把字符串s中的所有字符都设置成字符c

int main()
{
    char s[]="(!@&*#$^*@#&^(!@#*))";
    strset(s,'%');
    printf("%s",s);
    //输出>>%%%%%%%%%%%%%%%%%%%%
}

关于string:

利用迭代器遍历

我们可以使用类似C中的数组形式访问 s[1]

也可以使用STL特有的迭代器访问:
string::iterator it;
for (it = s1.begin(); it != s1.end(); it++)
{
    cout << *it << endl;
}

重载运算符


    s1 = s2;
    s1 += s2;
    s1 = s2 + s3;
    s1 == s2;
    s1 = "s" + s2; //正确
    s1 = "s" + "s"; //错误,加号两边至少要有一个string类型的对象
    s1 = "s" + s2 + "s" + "s"; //正确+”的两边要保证至少有一个string类型,所以5正确,6错误。
由于在C/C++中,+的返回值还是原类型,所以第7行中,"s"+s2返回一个string类型,
因此string+“s”也是正确的,

插入insert

string s1 = "hello";
s1.insert(1,"ins");
 //从s1的1位置开始,插入"ins"字符串,即s1="hinsello";
s1.insert(1, "ins", 2);
//从s1的1位置开始,插入"ins"字符串的前2个字符,即s1="hinello";
s1.insert(1, "ins", 1, 2);
//从s1的1位置开始,插入"ins"字符串的从1位置开始的2个字符,即s1="hnsello";

iterator insert(iterator it, char c);//在it处插入字符c,返回插入后迭代器的位置

删除rease

iterator erase(iterator first, iterator last);
//删除[first,last)之间的所有字符,返回删除后迭代器的位置

查找find
注意nops


cout << s.find("aa", 0) << endl; 
//返回的是子串位置。第二个参数是查找的起始位置,如果找不到,就返回string::npos
if (s.find("aa1", 0) == string::npos)
{
    cout << "找不到该子串!" << endl;
}

部分选取substr

string ss = s.substr(i,k);//复制s从i开始k个字符给ss

.
.
.
一道利用STL实现字符串哈希的例题
Businesses like to have memorable telephone numbers. One way to make a telephone number memorable is to have it spell a memorable word or phrase. For example, you can call the University of Waterloo by dialing the memorable TUT-GLOP. Sometimes only part of the number is used to spell a word. When you get back to your hotel tonight you can order a pizza from Gino’s by dialing 310-GINO. Another way to make a telephone number memorable is to group the digits in a memorable way. You could order your pizza from Pizza Hut by calling their ``three tens’’ number 3-10-10-10.

The standard form of a telephone number is seven decimal digits with a hyphen between the third and fourth digits (e.g. 888-1200). The keypad of a phone supplies the mapping of letters to numbers, as follows:

A, B, and C map to 2
D, E, and F map to 3
G, H, and I map to 4
J, K, and L map to 5
M, N, and O map to 6
P, R, and S map to 7
T, U, and V map to 8
W, X, and Y map to 9

There is no mapping for Q or Z. Hyphens are not dialed, and can be added and removed as necessary. The standard form of TUT-GLOP is 888-4567, the standard form of 310-GINO is 310-4466, and the standard form of 3-10-10-10 is 310-1010.

Two telephone numbers are equivalent if they have the same standard form. (They dial the same number.)

Your company is compiling a directory of telephone numbers from local businesses. As part of the quality control process you want to check that no two (or more) businesses in the directory have the same telephone number.

Input
The input will consist of one case. The first line of the input specifies the number of telephone numbers in the directory (up to 100,000) as a positive integer alone on the line. The remaining lines list the telephone numbers in the directory, with each number alone on a line. Each telephone number consists of a string composed of decimal digits, uppercase letters (excluding Q and Z) and hyphens. Exactly seven of the characters in the string will be digits or letters.
Output
Generate a line of output for each telephone number that appears more than once in any form. The line should give the telephone number in standard form, followed by a space, followed by the number of times the telephone number appears in the directory. Arrange the output lines by telephone number in ascending lexicographical order. If there are no duplicates in the input print the line:

No duplicates.
Sample Input
12
4873279
ITS-EASY
888-4567
3-10-10-10
888-GLOP
TUT-GLOP
967-11-11
310-GINO
F101010
888-1200
-4-8-7-3-2-7-9-
487-3279
Sample Output
310-1010 2
487-3279 4
888-4567 3

题意:所有字母转成相应数字,查一下有多少号码出现了不止一次,按字典序输出号码和出现次数。
用map来查重,用迭代器遍历。

#include<cstdio>
#include<iostream>
#include<map>
#include<cstring> 
#define LL long long

using namespace std; 
int n;
int l,sum,t; 
int cal(char c)
{
    switch(c){
    case 'A':
    case 'B':
    case 'C': return 2;
    case 'D':
    case 'E':
    case 'F': return 3;
    case 'G':
    case 'H':
    case 'I': return 4;
    case 'J':
    case 'K':
    case 'L': return 5;
    case 'M':
    case 'N':
    case 'O': return 6;
    case 'P':
    case 'R':
    case 'S': return 7;
    case 'T':
    case 'U':
    case 'V': return 8;
    case 'W':
    case 'X':
    case 'Y': return 9;
    }
}

int main()
{
	map<int,int>s;
	s.clear();
//	freopen("in.txt","r",stdin);
	cin>>n;
	while(n)
	{
		char a[50];
		scanf("%s",a);
		int sum = 0;
		for(int i=0;i<strlen(a);i++)
		{
			if(a[i]!='-')
			{
				if(a[i]>='A'&&a[i]<='Z')
				{
					sum = sum*10 + cal(a[i]); 
				} 
				else sum = sum*10 +(a[i] - '0');
		 } 
		s[sum]++;
		n--;
	 } 
	 bool pd = false;
	 for(map<int,int>::iterator i=s.begin();i!=s.end();i++)//迭代器访问map 
	 {
	 	if(i->second > 1)
	 	{
	 		printf("%03d-%04d %d\n", i->first / 10000, i->first % 10000, i->second);
	 		//利用STL实现自动补上前导零 
			 pd = true;
		 }
	 }
	 if(pd==false) printf("No duplicates.\n");
 } 

2.kmp算法

关于看xx算法 kmp算法的解析网上特别特别多,但各个博客中关于next数组第一位是0还是-1,数组从1开始还是从零开始规定都不太一样(就因为这个高一那会看这个算法的时候懵了整整一天 )。关于解析的话,我找了一个漫画解析感觉还不戳。

现规定在我的code中next数组第一个数值是-1,字符串数组从零开始计算。

点击进入漫画解析kmp

漫画直接copy过来了。

.
在这里插入图片描述
模板 & 例题1luoguP3375 【模板】KMP字符串匹配
题面

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
char a[1000010],b[1000010];
int n,m,la,lb,t,N[1000010];
void next()//构造next数组
{
	N[0]=-1;
	int j = -1;
	for(int i=1;i<la;i++)
	{
		while(b[i]!=b[j+1]&&j>-1)
		{
			j = N[j];
		}
		if(b[i]==b[j+1])
		{
			j++;
		}
		N[i] = j;
	}
}
void kmp()//kmp本体
{
	int i=0,j=-1;
	while(i<=la)
	{
		while(a[i]!=b[j+1]&&j>-1)
		{ 
			j = N[j];
		}
		if(a[i]==b[j+1]) 
		{
			j++;
			if(j==lb-1)
			{
				cout<<i-lb+2<<endl;
				j = N[j];
			}
			
		}
		i++;
	}
}
int main()
{
	scanf("%s %s",a,b);
	la = strlen(a);
	lb = strlen(b);
	next();
	kmp();
	for(int i=0;i<lb;i++)
	{
		printf("%d ",N[i]+1);
	}
	return 0;
}

.
.
.

例题2

The Genographic Project is a research partnership between IBM and The National Geographic Society that is analyzing DNA from hundreds of thousands of contributors to map how the Earth was populated.

As an IBM researcher, you have been tasked with writing a program that will find commonalities amongst given snippets of DNA that can be correlated with individual survey information to identify new genetic markers.

A DNA base sequence is noted by listing the nitrogen bases in the order in which they are found in the molecule. There are four bases: adenine (A), thymine (T), guanine (G), and cytosine ©. A 6-base DNA sequence could be represented as TAGACC.

Given a set of DNA base sequences, determine the longest series of bases that occurs in all of the sequences.
Input
Input to this problem will begin with a line containing a single integer n indicating the number of datasets. Each dataset consists of the following components:
A single positive integer m (2 <= m <= 10) indicating the number of base sequences in this dataset.
m lines each containing a single base sequence consisting of 60 bases.
Output
For each dataset in the input, output the longest base subsequence common to all of the given base sequences. If the longest common subsequence is less than three bases in length, display the string “no significant commonalities” instead. If multiple subsequences of the same longest length exist, output only the subsequence that comes first in alphabetical order.

Sample Input

3
2
GATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATA

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

3
GATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATA

GATACTAGATACTAGATACTAGATACTAAAGGAAAGGGAAAAGGGGAAAAAGGGGGAAAA

GATACCAGATACCAGATACCAGATACCAAAGGAAAGGGAAAAGGGGAAAAAGGGGGAAAA

3
CATCATCATCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC

ACATCATCATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

AACATCATCATTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT

Sample Output
no significant commonalities
AGATAC
CATCATCAT

题意:查一下各组里各条链有没有三个及以上的重合部分。
idea:直接对第一条链各部分作为模式串kmp,但是!!考虑到不用查出现了几次和在哪里出现,所以可直接用STL函数解决我们STL选手永不为奴 )(本shazi打完kmp才想起来还有个函数叫strsub

STL函数版本:

s[j].find(ss)==string::npos//s中没有ss时候回返回一个npos
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#define LL long long
using namespace std;
string s[11],str,ss;
int main()
{
	int n,m;
	cin>>n;
	while(n)
	{
		cin>>m;
		for(int i=1;i<=m;i++)
		{
			cin>>s[i];
		}
		str = "no significant commonalities";
        for(int k=3;k<=60;k++)
        {
        	for(int i=0;i<=60-k;i++)
        	{
        		bool pd = false;
        		string ss = s[1].substr(i,k);
        		for(int j=2;j<=m;j++)
        		{
        			if(s[j].find(ss)==string::npos)
        			{
        				pd = true;
        				break;
        			}
        		}
        		
        		if(pd == false) 
        		{
        			if(str == "no significant commonalities")
        			{
        				str = ss;
        			}
        			else if( str.size() < ss.size() )
        			{
        				str = ss;
        			}
        			else if( str.size() == ss.size() )
        			{
        				str = min( str,ss );
        			}
        		} 
        		
        	}
        }
        cout<<str<<endl;
		n--;
	}
	return 0;
}

kmp版本

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#define LL long long
using namespace std;
string s[11],str,ss,a,b;
int la,lb,N[1000];
void next()
{
	N[0]=-1;
	int j = -1;
	for(int i=1;i<lb;i++)
	{
		while(b[i] != b[j+1]&&j>-1)
		{
			j = N[j];
		}
		if(b[j+1]==b[i])
		{
			j++;
		}
		N[i] = j;
	}
}
bool kmp()
{
	int i=0,j=-1;
	while(i<la)
	{
		while(a[i]!=b[j+1]&&j>-1)
		{
			j = N[j];
		}
		if(a[i]==b[j+1]) 
		{
			j++;
			if(j==lb-1)
			{
				return true;
			}
		}
		i++;
	}
	return false;
}
int main()
{
//	freopen("in.txt","r",stdin);
	int n,m;
	cin>>n;
	while(n)
	{
		cin>>m;
		for(int i=1;i<=m;i++)
		{
			cin>>s[i];
		}
		str = "no significant commonalities";
        for(int k=3;k<=60;k++)
        {
        	for(int i=0;i<=60-k;i++)
        	{
        		bool pd = false;
        		b = s[1].substr(i,k);
        		lb = b.size();
				next();
        		for(int j=2;j<=m;j++)
        		{
        			
        			a = s[j];
					la = a.size();
        			if( kmp() ==false ) 
        			{
        				pd = true;
        				break;
        			}
        		}
        		
        		if(pd == false) 
        		{
        			if(str == "no significant commonalities")
        			{
        				str = b;
        			}
        			else if( str.size() < b.size() )
        			{
        				str = b;
        			}
        			else if( str.size() ==b.size() )
        			{
        				str = min( str,b );
        			}
        		} 
        		
        	}
        }
        cout<<str<<endl;
		n--;
	}
	return 0;
}

例题3next数组的性质
It is well known that AekdyCoin is good at string problems as well as number theory problems. When given a string s, we can write down all the non-empty prefixes of this string. For example:
s: “abab”
The prefixes are: “a”, “ab”, “aba”, “abab”
For each prefix, we can count the times it matches in s. So we can see that prefix “a” matches twice, “ab” matches twice too, “aba” matches once, and “abab” matches once. Now you are asked to calculate the sum of the match times for all the prefixes. For “abab”, it is 2 + 2 + 1 + 1 = 6.
The answer may be very large, so output the answer mod 10007.
Input
The first line is a single integer T, indicating the number of test cases.
For each case, the first line is an integer n (1 <= n <= 200000), which is the length of string s. A line follows giving the string s. The characters in the strings are all lower-case letters.
Output
For each case, output only one number: the sum of the match times for all the prefixes of s mod 10007.
Sample Input
1
4
abab
Sample Output
6

题意:给T组数据,每组数据给一个长度为n的字符串s。求字符串每个前缀出现的次数和,结果mod 10007。
idea:利用kmp算法的next数组可以很好的解决这个问题, next数组存放的是字符串的前缀和后缀能匹配的字符个数的最大值。

对于i,(1 <= i <=n),n是字符串s的长度

    如果next[i]  == -1 则表示   由s的前i个字符组成的字符串的所有后缀肯定和其前缀不匹配。

    否则      由s的前i个字符组成的字符串存在某个前缀和后缀匹配的情况,也就是该前缀的出现的次数应该加上1。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int qwq = 10007;
char a[200100],b[200100];
int N[200100],la,lb,ans=0;
void Getnext()
{
	N[0] = -1;
	int j =-1;
	for(int i=1;i<lb;i++)
	{
		while(b[i]!=b[j+1]&&j>-1)
		{
			j = N[j];
		}
		if(b[i]==b[j+1])
		{
			j++;
		}
		N[i] = j;
	}
}
void kmp()
{
	int i=0,j=-1;
	while(i<la)
	{
		while(a[i]!=b[j+1]&&j>-1)
		{
			j = N[j];
		}
		if(a[i]==b[j+1])
		{
			j++;
			if(j==lb-1)
			{
				j = N[j];
				ans++;
				ans %= qwq;
			}
		}
		i++;
	}
}
int main()
{
	int n,m;
	cin>>n;
	while(n)
	{
		ans = 0;
		cin>>m;
		scanf("%s",a);
		strcpy(b,a);
		la = m;
		lb = m;
		Getnext();
		int t = lb;
//		for(int i=0;i<m;i++) cout<<N[i]<<" ";
//		kmp();
		for(int i=0;i<lb;i++)
		{
			if(N[i]!=-1) ans++;
			ans %=qwq;
		}
		cout<<(ans+m)%qwq<<endl;
		n--;
	}
	return 0;
}

例题4
在这里插入图片描述
在这里插入图片描述
 题意:给你两个串只有0和1的T,S,其中S可以进行循环右移,问你S里面是否存在两个长度都为|S|的子串它们的异或结果等于T。
 
idea:根据xor的性质,可以先把T和S→移动过的一个串进行XOR,再到S右移的子串中查询。刚开始我XOR完居然去和T匹配,改到人傻了

直接把两个S拼起来就可以取到任意移动过的串了

#pragma GCC optimize(2)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define M 1000002
#define XOR(x,y) ( x==y ? '0' : '1')
using namespace std;
char s[M],t[M],b[M];
int N[M],ls,lt,lss;
bool pd;
void Getnext()
{
//	memset(N,-1,sizeof(N));
	N[0]=-1;
	int j = -1;
	for(int i=1;i<lt;i++)
	{
		while(b[i]!=b[j+1]&&j>-1)
		{
			j = N[j];
		}
		if(b[i]==b[j+1])
		{
			j++;
		}
		N[i] = j;
	}
}
bool kmp(int f)
{
	for(int k=0;k<lt;k++)
	{
		b[k] = XOR( t[k],s[f+k] );
	}
	Getnext();
	int i=0,j=-1;
	while(i<=lss)
	{
		while(s[i]!=b[j+1]&&j>-1)
		{
			j = N[j];
		}
		if(s[i]==b[j+1])
		{
			j++;
			if(j==lt-1)
			{
				return true;
			}
		}
		i++;
	}
	return false;
}
int main()
{
//	freopen("in.txt","r",stdin);
	while( scanf("%s %s",t,s)!=EOF )
	{
		pd = false;
		ls = strlen(s);
		lt = strlen(t);
		lss = 2*ls;
		s[ls*2]='\0';
		for(int i=0;i<ls;i++)
		{
			s[ls+i] = s[i];
		}
		for(int i=0;i<ls;i++)
		{
			pd = kmp(i);
			if(pd==true) break;
		}
		if(pd==true) printf("Yes\n");
		else printf("No\n");
	}
	
	return 0;
}

.
.
.

3.manacher算法

推荐马拉车算法一个还不错的教程

manacher算法是用来处理回文字符串问题一个线性算法,时间复杂度为O(n)。

回文串的个数是可奇可偶的,碰上奇数的回文串还可以,如果是偶数的回文串没有着脚点,那就很恼人了。所以马拉车算法会对字符串进行预先处理,然后再求最长的回文串。首先用字符串中没有出现过的字符来表示串中每个元素的间隔,而为了防止在访问时出现越界情况,需要在串首和串尾再加上额外的特殊字符。

例如:原串为ababab;处理完之后就是#a#b#a#b#a#b#; 其实对于最后一个$,也可以不加,因为字符串的最后一个字符是‘\0’就相当于一个特殊字符了。
2、接下来就是在新串中找以每一个字符为中心的回文串就可以了。manacher算法的思想就是从左到右求出以每个字符为中心的最长回文串。设能延伸到最右边的字符串的中心位置为id,该字符串最右端的位置为mx,pal数组来储存此处回文串的长度。因为回文串有对称的性质,所以后边的字符串可以通过对称来直接求得其长度(当然前边的还是需要乖乖的遍历求出来的)。

3、对于遍历到的字符(下标设为i),一共会有三种情况;

(1)i<=mx;
在这里插入图片描述
该情况下就万事大吉了,直接把2*id-i处串的长度,复制给i就OK了。

(2)i<=mx
同样是这种情况但是出现i处的回文串超出了mx的情况
在这里插入图片描述
最右端超出了mx的范围,出现什么情况就不好说了,所以只能暴力一下,然后更新mx的大小就可以了

(3)i>mx
这种情况直接暴力,求此处回文串的长度即可。

模板&例题 luoguP3805 【模板】manacher算法
在这里插入图片描述

 #include<iostream>
#include<cstring>
#include<cstdio>
#define LL long long
#define N 11000100
using namespace std; 
char a[N],ch[2*N+10];
int mx,p[2*N+10],sum=0,ans=0,len,pos=0;
void manacher()
{
	for(int i=1;i<=2*len+1;i++)
	{
		int j;
		if(i<mx)
		{
			j = 2*pos-i;
			p[i] = min( p[j] , mx-i );
		}
		else
		{
			if(ch[i]=='#') p[i] = 1;
			else p[i] = 2;
		}
		while( ch[ i-p[i] ] == ch[ i+p[i] ] ) p[i]++;//向两侧扩展,找最大。
		if(i+p[i]-1 > mx)
		{
			mx = i+p[i]-1;
			pos = i;
		}
		ans = max(ans,p[i]-1);
	}
}
int main()
{
	scanf("%s",a+1);
	len = strlen(a+1);
	ch[0] = '@';
	ch[2*len + 2] = '&';
	for(int i=1;i<=2*len+1;i++)
	{
		if(i%2==1)
		{
			ch[i] = '#';
		}
		else
		{
			ch[i] = a[i/2];
		}
	}
	manacher();
	cout<<ans;
	return 0;
}

.
.
.

4.拓展KMP(Z函数)

拓展kmp极佳讲解

例题导入
在这里插入图片描述
证明部分的我是搬运工

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

if(i+nxt[i-p0]<extend[p0]+p0)extend[i]=nxt[i-p0];
//i相当于k+1
//nxt[i-p0]相当于L
//extend[p0]+p0相当于p
//因为在代码里我是从0开始记字符串的,所以本应在小于号左侧减1,现在不用了。

在这里插入图片描述

int now=extend[p0]+p0-i;
now=max(now,0);//这里是防止i>p
while(t[now]==s[i+now]&&now<(int)t.size()&&now+i<(int)s.size())now++;//暴力求解的过程
extend[i]=now;
p0=i;//更新p0

在这里插入图片描述
总的代码:

#include<bits/stdc++.h>

#define N 1000010 

using namespace std;

int q,nxt[N],extend[N];
string s,t;

void getnxt()
{
    nxt[0]=t.size();//nxt[0]一定是T的长度
    int now=0;
    while(t[now]==t[1+now]&&now+1<(int)t.size())now++;//这就是从1开始暴力
    nxt[1]=now;
    int p0=1;
    for(int i=2;i<(int)t.size();i++)
    {
        if(i+nxt[i-p0]<nxt[p0]+p0)nxt[i]=nxt[i-p0];//第一种情况
        else
        {//第二种情况
            int now=nxt[p0]+p0-i;
            now=max(now,0);//这里是为了防止i>p的情况
            while(t[now]==t[i+now]&&i+now<(int)t.size())now++;//暴力
            nxt[i]=now;
            p0=i;//更新p0
        }
    }
}

void exkmp()
{
    getnxt();
    int now=0;
    while(s[now]==t[now]&&now<min((int)s.size(),(int)t.size()))now++;//暴力
    extend[0]=now;
    int p0=0;
    for(int i=1;i<(int)s.size();i++)
    {
        if(i+nxt[i-p0]<extend[p0]+p0)extend[i]=nxt[i-p0];//第一种情况
        else
        {//第二种情况
            int now=extend[p0]+p0-i;
            now=max(now,0);//这里是为了防止i>p的情况
            while(t[now]==s[i+now]&&now<(int)t.size()&&now+i<(int)s.size())now++;//暴力
            extend[i]=now;
            p0=i;//更新p0
        }
    }
}

int main()
{
    cin>>s>>t;
    exkmp();
    int len=t.size();
    for(int i=0;i<len;i++)printf("%d ",nxt[i]);//输出nxt
    puts("");
    len=s.size();
    for(int i=0;i<len;i++)printf("%d ",extend[i]);//输出extend
    return 0;
}

.
.
.

5.AC自动机&&tire树

当年GDKOI就被这玩意干翻过----蒟蒻如是说道
这部分让我手敲解析不如neng si我算了
直接呈上dalao讲解

老模板题了

在这里插入图片描述


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
char txt[1000005],ch[160][80];
int cishu[160],num=0,maxn,n;
struct node
{
	int son[30],fail,x,cot;
	bool have;
}trie[20005];
void re()
{
	num=0;
	maxn=0;
	memset(trie,0,sizeof(trie));
	memset(cishu,0,sizeof(cishu));
	trie[0].fail=-1;
}
void insert(int j)
{
	int len=strlen(ch[j]);
	int a=0,b;
	for(int i=0;i<len;i++)
	{
		b=ch[j][i]-'a';
		if(!trie[a].son[b]) 
		trie[a].son[b]=++num;
		a=trie[a].son[b];
	}
	trie[a].have=1;
	trie[a].x=j;
}
void getfail()
{
	queue<int>dui;
	dui.push(0);
	int a;
	while(!dui.empty())
	{
		a=dui.front();
		dui.pop();
		for(int i=0;i<26;i++)
		{
			if(trie[a].son[i])
			{
				if(a==0)
				{
					trie[trie[a].son[i]].fail=0;
				}
			else 
			{
				int p=trie[a].fail;
				while(p!=-1)
				{
					if(trie[p].son[i])
					{
						trie[trie[a].son[i]].fail=trie[p].son[i];
						break;
					}
					p=trie[p].fail;
				}
				if(p==-1)
				{
					trie[trie[a].son[i]].fail=0;
				}
			}
			dui.push(trie[a].son[i]);
			}
		
		}
	}
}
void pp()
{
	int len=strlen(txt);
	int a=0,b;
	for(int i=0;i<len;i++)
	{
		b=txt[i]-'a';
		while(a!=0&&trie[a].son[b]==0)
		{
			a=trie[a].fail;
		}
		if(trie[a].son[b])
		a=trie[a].son[b];
		int p=a;
		while(p!=0)
		{
			if(trie[p].have)
			{
				++cishu[trie[p].x];
				maxn=max(maxn,cishu[trie[p].x]);
			}
			p=trie[p].fail;
		}
	}
}
int main()
{
	//freopen("hh.txt","r",stdin);
	while(scanf("%d",&n))
	{
		if(!n)
		{
			return 0;
		}
		re();
		for(int i=1;i<=n;i++)
		{
			scanf("%s",&ch[i]);
			insert(i);
		}
		getfail();
		cin>>txt;
		pp();
		cout<<maxn<<endl;
		for(int i=1;i<=n;i++)
		{
			if(cishu[i]==maxn)
			{
				printf("%s\n",ch[i]);
			}
		}
	}
	return 0;
}

还是一条不存在的分割线

在这里插入图片描述

写在后面:
刚好今天是NOIP复赛日,不禁让我回想起来2018年那一场NOIP,当时高三的我为了这场比赛翘了一个月晚修在机房训练,只为弥补高二那年初赛因为一些意外无法参加无缘复赛的遗憾。最后的结果也算是对得起从高一才开始学OI的我两年的OI生涯了。
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值