反手套一波回文树模板加例题就很舒服

往上偷的板子,用起来蛮不错嘿嘿嘿。

#include
using namespace std;
const int MAXN = 300005;
const int N = 26;
const int MOD = 1e9 + 7;
struct Palindromic_Tree {
	int next[MAXN][N];//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成  
	int fail[MAXN];//fail指针,失配后跳转到fail指针指向的节点  
	int cnt[MAXN]; //表示节点i表示的本质不同的串的个数(建树时求出的不是完全的,最后count()函数跑一遍以后才是正确的) 
	int num[MAXN]; //表示以节点i表示的最长回文串的最右端点为回文串结尾的回文串个数
	int len[MAXN];//len[i]表示节点i表示的回文串的长度(一个节点表示一个回文串)
	int S[MAXN];//存放添加的字符  
	int last;//指向新添加一个字母后所形成的最长回文串表示的节点。
	int n;//表示添加的字符个数。
	int p;//表示添加的节点个数。
	int psum[MAXN];//表示以节点i表示的最长回文串的最右端点为回文串结尾的所有回文串的长度和

	int newnode(int l) {//新建节点  
		for (int i = 0; i < N; ++i) next[p][i] = 0;
		cnt[p] = 0;
		num[p] = 0;
		len[p] = l;
		return p++;
	}

	void init() {//初始化  
		p = 0;
		newnode(0);
		newnode(-1);
		last = 0;
		n = 0;
		S[n] = -1;//开头放一个字符集中没有的字符,减少特判  
		fail[0] = 1;
		psum[0] = 0; psum[1] = 0;
	}

	int get_fail(int x) {//和KMP一样,失配后找一个尽量最长的  
		while (S[n - len[x] - 1] != S[n]) x = fail[x];
		return x;
	}

	void add(int c) {
		c -= 'a';
		S[++n] = c;
		int cur = get_fail(last);//通过上一个回文串找这个回文串的匹配位置  
		if (!next[cur][c]) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串  
			int now = newnode(len[cur] + 2);//新建节点  
			fail[now] = next[get_fail(fail[cur])][c];//和AC自动机一样建立fail指针,以便失配后跳转  
			next[cur][c] = now;
			num[now] = num[fail[now]] + 1;
			psum[now] = ((long long)psum[fail[now]] + len[now]) % MOD;
		}
		last = next[cur][c];
		cnt[last] ++;
	}

	void count() {
		for (int i = p - 1; i >= 0; --i) cnt[fail[i]] += cnt[i];
		//父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!  
	}
};
Palindromic_Tree pam;

HYSBZ - 3676 

考虑一个只包含小写拉丁字母的字符串s。我们定义s的一个子串t的“出
现值”为t在s中的出现次数乘以t的长度。请你求出s的所有回文子串中的最
大出现值。

Input

输入只有一行,为一个只包含小写字母(a -z)的非空字符串s。

Output


输出一个整数,为逝查回文子串的最大出现值。

Sample Input

【样例输入l】 abacaba 【样例输入2] www

Sample Output

【样例输出l】 7 【样例输出2] 4

#include
using namespace std;
const int MAXN = 300005;
const int N = 26;

struct Palindromic_Tree {
	int next[MAXN][N];//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成  
	int fail[MAXN];//fail指针,失配后跳转到fail指针指向的节点  
	int cnt[MAXN]; //表示节点i表示的本质不同的串的个数(建树时求出的不是完全的,最后count()函数跑一遍以后才是正确的) 
	int num[MAXN]; //表示以节点i表示的最长回文串的最右端点为回文串结尾的回文串个数
	int len[MAXN];//len[i]表示节点i表示的回文串的长度(一个节点表示一个回文串)
	int S[MAXN];//存放添加的字符  
	int last;//指向新添加一个字母后所形成的最长回文串表示的节点。
	int n;//表示添加的字符个数。
	int p;//表示添加的节点个数。

	int newnode(int l) {//新建节点  
		for (int i = 0; i < N; ++i) next[p][i] = 0;
		cnt[p] = 0;
		num[p] = 0;
		len[p] = l;
		return p++;
	}

	void init() {//初始化  
		p = 0;
		newnode(0);
		newnode(-1);
		last = 0;
		n = 0;
		S[n] = -1;//开头放一个字符集中没有的字符,减少特判  
		fail[0] = 1;//最好放外面不然好多数组都要清零初始化,比如这个
	}

	int get_fail(int x) {//和KMP一样,失配后找一个尽量最长的  
		while (S[n - len[x] - 1] != S[n]) x = fail[x];
		return x;
	}

	void add(int c) {
		c -= 'a';
		S[++n] = c;
		int cur = get_fail(last);//通过上一个回文串找这个回文串的匹配位置  
		if (!next[cur][c]) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串  
			int now = newnode(len[cur] + 2);//新建节点  
			fail[now] = next[get_fail(fail[cur])][c];//和AC自动机一样建立fail指针,以便失配后跳转  
			next[cur][c] = now;
			num[now] = num[fail[now]] + 1;
		}
		last = next[cur][c];
		cnt[last] ++;
	}

	void count() {
		for (int i = p - 1; i >= 0; --i) cnt[fail[i]] += cnt[i];
		//父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!  
	}
};
Palindromic_Tree pam;
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	string m; cin >> m;
	pam.init();
	for (int i = 0; i < m.length(); i++) {
		pam.add(m[i]);
	}
	pam.count();
	long long ret = 0;
	for (int i = 2; i < pam.p; i++) {
		ret = max((long long)pam.len[i] * pam.cnt[i], ret);
	}
	cout << ret << endl;
	return 0;
}

 

UVALive - 7041 

求a中每个回文串在b中出现的次数的和

奖励两课树,分别作用于a,b,然后进行从节点0和1分别向下dfs即可

#include
using namespace std;
const int MAXN = 3600005;
const int N = 26;

struct Palindromic_Tree {
	int next[MAXN][N];//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成  
	int fail[MAXN];//fail指针,失配后跳转到fail指针指向的节点  
	int cnt[MAXN]; //表示节点i表示的本质不同的串的个数(建树时求出的不是完全的,最后count()函数跑一遍以后才是正确的) 
	int num[MAXN]; //表示以节点i表示的最长回文串的最右端点为回文串结尾的回文串个数
	int len[MAXN];//len[i]表示节点i表示的回文串的长度(一个节点表示一个回文串)
	int S[MAXN];//存放添加的字符  
	int last;//指向新添加一个字母后所形成的最长回文串表示的节点。
	int n;//表示添加的字符个数。
	int p;//表示添加的节点个数。

	int newnode(int l) {//新建节点  
		for (int i = 0; i < N; ++i) next[p][i] = 0;
		cnt[p] = 0;
		num[p] = 0;
		len[p] = l;
		return p++;
	}

	void init() {//初始化  
		p = 0;
		newnode(0);
		newnode(-1);
		last = 0;
		n = 0;
		S[n] = -1;//开头放一个字符集中没有的字符,减少特判  
		fail[0] = 1;
	}

	int get_fail(int x) {//和KMP一样,失配后找一个尽量最长的  
		while (S[n - len[x] - 1] != S[n]) x = fail[x];
		return x;
	}

	void add(int c) {
		c -= 'a';
		S[++n] = c;
		int cur = get_fail(last);//通过上一个回文串找这个回文串的匹配位置  
		if (!next[cur][c]) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串  
			int now = newnode(len[cur] + 2);//新建节点  
			fail[now] = next[get_fail(fail[cur])][c];//和AC自动机一样建立fail指针,以便失配后跳转  
			next[cur][c] = now;
			num[now] = num[fail[now]] + 1;
		}
		last = next[cur][c];
		cnt[last] ++;
	}

	void count() {
		for (int i = p - 1; i >= 0; --i) cnt[fail[i]] += cnt[i];
		//父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!  
	}
};
Palindromic_Tree pam1, pam2;
long long ans = 0;
void dfs(int u, int v) {
	for (int i = 0; i < 26; i++) {
		int x = pam1.next[u][i];
		int y = pam2.next[v][i];
		if (x && y) {
			ans += 1LL * pam1.cnt[x] * pam2.cnt[y];
			dfs(x, y);
		}
	}
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int te, cas = 1;
	cin >> te;
	while(te--){
		string n, m; cin >> n >> m;
		pam1.init(); pam2.init(); ans = 0;
		for (int i = 0; i < n.length(); i++) {
			pam1.add(n[i]);
		}
		for (int j = 0; j < m.length(); j++) {
			pam2.add(m[j]);
		}
		pam1.count(); pam2.count();
		dfs(0, 0); dfs(1, 1);
		cout << "Case #" << cas << ": " << ans << "\n"; cas++;
	}
	return 0;
}

 

HDU - 3068 

给出一个只由小写英文字符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
#include
using namespace std;
const int MAXN = 200005;
const int N = 26;
long long ans = 0;
struct Palindromic_Tree {
	int next[MAXN][N];//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成  
	int fail[MAXN];//fail指针,失配后跳转到fail指针指向的节点  
	int cnt[MAXN]; //表示节点i表示的本质不同的串的个数(建树时求出的不是完全的,最后count()函数跑一遍以后才是正确的) 
	int num[MAXN]; //表示以节点i表示的最长回文串的最右端点为回文串结尾的回文串个数
	int len[MAXN];//len[i]表示节点i表示的回文串的长度(一个节点表示一个回文串)
	int S[MAXN];//存放添加的字符  
	int last;//指向新添加一个字母后所形成的最长回文串表示的节点。
	int n;//表示添加的字符个数。
	int p;//表示添加的节点个数。

	int newnode(int l) {//新建节点  
		for (int i = 0; i < N; ++i) next[p][i] = 0;
		cnt[p] = 0;
		num[p] = 0;
		len[p] = l; ans = max(ans, 1LL * l);
		return p++;
	}

	void init() {//初始化  
		p = 0;
		newnode(0);
		newnode(-1);
		last = 0;
		n = 0;
		S[n] = -1;//开头放一个字符集中没有的字符,减少特判  
		fail[0] = 1;
	}

	int get_fail(int x) {//和KMP一样,失配后找一个尽量最长的  
		while (S[n - len[x] - 1] != S[n]) x = fail[x];
		return x;
	}

	void add(int c) {
		c -= 'a';
		S[++n] = c;
		int cur = get_fail(last);//通过上一个回文串找这个回文串的匹配位置  
		if (!next[cur][c]) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串  
			int now = newnode(len[cur] + 2);//新建节点  
			fail[now] = next[get_fail(fail[cur])][c];//和AC自动机一样建立fail指针,以便失配后跳转  
			next[cur][c] = now;
			num[now] = num[fail[now]] + 1;
		}
		last = next[cur][c];
		cnt[last] ++;
	}

	void count() {
		for (int i = p - 1; i >= 0; --i) cnt[fail[i]] += cnt[i];
		//父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!  
	}
};
Palindromic_Tree pam1;
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	string n, m;
	while(cin>>n){
		pam1.init(); ans = 1;
		for (int i = 0; i < n.length(); i++) {
			pam1.add(n[i]);
		}
		cout << ans << "\n";
	}
	return 0;
}

 

HDU - 3948 

输出一个字符串不同的回文串的数量

#include
using namespace std;
const int MAXN = 200005;
const int N = 26;
long long ans = 0;
struct Palindromic_Tree {
	int next[MAXN][N];//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成  
	int fail[MAXN];//fail指针,失配后跳转到fail指针指向的节点  
	int cnt[MAXN]; //表示节点i表示的本质不同的串的个数(建树时求出的不是完全的,最后count()函数跑一遍以后才是正确的) 
	int num[MAXN]; //表示以节点i表示的最长回文串的最右端点为回文串结尾的回文串个数
	int len[MAXN];//len[i]表示节点i表示的回文串的长度(一个节点表示一个回文串)
	int S[MAXN];//存放添加的字符  
	int last;//指向新添加一个字母后所形成的最长回文串表示的节点。
	int n;//表示添加的字符个数。
	int p;//表示添加的节点个数。

	int newnode(int l) {//新建节点  
		for (int i = 0; i < N; ++i) next[p][i] = 0;
		cnt[p] = 0;
		num[p] = 0;
		len[p] = l; ans = max(ans, 1LL * l);
		return p++;
	}

	void init() {//初始化  
		p = 0;
		newnode(0);
		newnode(-1);
		last = 0;
		n = 0;
		S[n] = -1;//开头放一个字符集中没有的字符,减少特判  
		fail[0] = 1;
	}

	int get_fail(int x) {//和KMP一样,失配后找一个尽量最长的  
		while (S[n - len[x] - 1] != S[n]) x = fail[x];
		return x;
	}

	void add(int c) {
		c -= 'a';
		S[++n] = c;
		int cur = get_fail(last);//通过上一个回文串找这个回文串的匹配位置  
		if (!next[cur][c]) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串  
			int now = newnode(len[cur] + 2);//新建节点  
			fail[now] = next[get_fail(fail[cur])][c];//和AC自动机一样建立fail指针,以便失配后跳转  
			next[cur][c] = now;
			num[now] = num[fail[now]] + 1;
		}
		last = next[cur][c];
		cnt[last] ++;
	}

	void count() {
		for (int i = p - 1; i >= 0; --i) cnt[fail[i]] += cnt[i];
		//父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!  
	}
};
Palindromic_Tree pam1;
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	string n, m; int cas = 1, te; 
	cin >> te;
	while(te--){
		cin >> n;
		pam1.init(); ans = 1;
		for (int i = 0; i < n.length(); i++) {
			pam1.add(n[i]);
		}
		pam1.count();
		cout << "Case #" << cas << ": " << pam1.p - 2 << "\n"; cas++;
	}
	return 0;
}

HYSBZ - 2565 

顺序和逆序读起来完全一样的串叫做回文串。比如 acbca 是回文串,而 abc 不是( abc 的顺序为 “abc” ,逆序为 “cba” ,不相同)。
输入长度为 n 的串 S ,求 S 的最长双回文子串 T, 即可将 T 分为两部分 X , Y ,( |X|,|Y|≥1 )且 X 和 Y 都是回文串。

求最长双回文子串长度。

对每一个位置而言ans = max(ans ,以此位置为结尾的最长回文串+以i+1为开头的最长回文串)双向分别建一次树就好了。

#include
using namespace std;
const int MAXN = 200005;
const int N = 26;
long long ans = 0;
struct Palindromic_Tree {
	int next[MAXN][N];//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成  
	int fail[MAXN];//fail指针,失配后跳转到fail指针指向的节点  
	int cnt[MAXN]; //表示节点i表示的本质不同的串的个数(建树时求出的不是完全的,最后count()函数跑一遍以后才是正确的) 
	int num[MAXN]; //表示以节点i表示的最长回文串的最右端点为回文串结尾的回文串个数
	int len[MAXN];//len[i]表示节点i表示的回文串的长度(一个节点表示一个回文串)
	int S[MAXN];//存放添加的字符  
	int last;//指向新添加一个字母后所形成的最长回文串表示的节点。
	int n;//表示添加的字符个数。
	int p;//表示添加的节点个数。

	int newnode(int l) {//新建节点  
		for (int i = 0; i < N; ++i) next[p][i] = 0;
		cnt[p] = 0;
		num[p] = 0;
		len[p] = l; ans = max(ans, 1LL * l);
		return p++;
	}

	void init() {//初始化  
		p = 0;
		newnode(0);
		newnode(-1);
		last = 0;
		n = 0;
		S[n] = -1;//开头放一个字符集中没有的字符,减少特判  
		fail[0] = 1;
	}

	int get_fail(int x) {//和KMP一样,失配后找一个尽量最长的  
		while (S[n - len[x] - 1] != S[n]) x = fail[x];
		return x;
	}

	void add(int c) {
		c -= 'a';
		S[++n] = c;
		int cur = get_fail(last);//通过上一个回文串找这个回文串的匹配位置  
		if (!next[cur][c]) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串  
			int now = newnode(len[cur] + 2);//新建节点  
			fail[now] = next[get_fail(fail[cur])][c];//和AC自动机一样建立fail指针,以便失配后跳转  
			next[cur][c] = now;
			num[now] = num[fail[now]] + 1;
		}
		last = next[cur][c];
		cnt[last] ++;
	}

	void count() {
		for (int i = p - 1; i >= 0; --i) cnt[fail[i]] += cnt[i];
		//父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!  
	}
};
Palindromic_Tree pam1, pam2;
int sum[100010], sum2[100010];
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	string n, m; int cas = 1, te;
	cin >> n;
	pam1.init(); ans = 1; pam2.init();
	for (int i = 0; i < n.length(); i++) {
		pam1.add(n[i]);
		sum[i] = pam1.len[pam1.last];
	}
	for (int i = n.length() - 1; i >= 0; i--) {
		pam2.add(n[i]);
		sum2[i] = pam2.len[pam2.last];
	}
	int ans = 0;
	for (int i = 0; i < n.length() - 1; i++) {
		ans = max(sum[i] + sum2[i + 1], ans);
	}
	cout << ans << "\n";
	return 0;
}

 

URAL - 1960 

对于字符串n,对于i(0-n.length()-1),输出前i个字符能够构成的回文串数量。

#include
#include
#include
using namespace std;
const int MAXN = 200005;
const int N = 26;
long long ans = 0;
struct Palindromic_Tree {
	int next[MAXN][N];//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成  
	int fail[MAXN];//fail指针,失配后跳转到fail指针指向的节点  
	int cnt[MAXN]; //表示节点i表示的本质不同的串的个数(建树时求出的不是完全的,最后count()函数跑一遍以后才是正确的) 
	int num[MAXN]; //表示以节点i表示的最长回文串的最右端点为回文串结尾的回文串个数
	int len[MAXN];//len[i]表示节点i表示的回文串的长度(一个节点表示一个回文串)
	int S[MAXN];//存放添加的字符  
	int last;//指向新添加一个字母后所形成的最长回文串表示的节点。
	int n;//表示添加的字符个数。
	int p;//表示添加的节点个数。

	int newnode(int l) {//新建节点  
		for (int i = 0; i < N; ++i) next[p][i] = 0;
		cnt[p] = 0;
		num[p] = 0;
		len[p] = l; ans = max(ans, 1LL * l);
		return p++;
	}

	void init() {//初始化  
		p = 0;
		newnode(0);
		newnode(-1);
		last = 0;
		n = 0;
		S[n] = -1;//开头放一个字符集中没有的字符,减少特判  
		fail[0] = 1;
	}

	int get_fail(int x) {//和KMP一样,失配后找一个尽量最长的  
		while (S[n - len[x] - 1] != S[n]) x = fail[x];
		return x;
	}

	void add(int c) {
		c -= 'a';
		S[++n] = c;
		int cur = get_fail(last);//通过上一个回文串找这个回文串的匹配位置  
		if (!next[cur][c]) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串  
			int now = newnode(len[cur] + 2);//新建节点  
			fail[now] = next[get_fail(fail[cur])][c];//和AC自动机一样建立fail指针,以便失配后跳转  
			next[cur][c] = now;
			num[now] = num[fail[now]] + 1;
		}
		last = next[cur][c];
		cnt[last] ++;
	}

	void count() {
		for (int i = p - 1; i >= 0; --i) cnt[fail[i]] += cnt[i];
		//父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!  
	}
};
Palindromic_Tree pam1;
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	string n, m; int cas = 1, te;
	cin >> n;
	pam1.init(); 
	for (int i = 0; i < n.length(); i++) {
		pam1.add(n[i]);
		cout << pam1.p - 2; if (i != n.length() - 1)cout << " ";
	}
	return 0;
}

 

你可能感兴趣的:(回文树)