zoj:Strings in the Pocket (马拉车)

题目大意:有两个字符串s,t,问能否将s的一段[l,r]子串翻转使得它等于t? 如果可以,问有多少段这样的子串使得翻转一次后s = t,注:只能翻转一次。

解法:简单题,分情况讨论:如果s 原本就等于 t,这个时候s中所有回文串的半径就是答案。若s 不等于 t,则找出不相等的那一段区间的最左边和最右边,check一下中间这段能否通过翻转使得相等,如果不能答案 = 0,否则可以像两边延申判断l,r 是否可以翻转使得s = t,答案为可以延申的长度。

这里留意一下马拉车统计答案的方式,只要明白算出来的东西是什么,可以打个表看一下,找规律然后统计。

#include<bits/stdc++.h>
using namespace std;
int ca;
const int maxn = 2e6 + 10;
char s[maxn],t[maxn];
char tmp[2 * maxn];
int len[2 * maxn],tot;
bool strcmp(char s[],char t[],int l,int r) {
	for(int i = l; i <= r; i++)
		if(s[i] != t[i]) return false;
	return true;
}
bool check(char s[],char t[],int l,int r) {
	for(int i = l; i <= r; i++) {
		if(s[i] != t[r - i + l]) return false;
	}
	return true;
}
void init(int p) {
	tot = 0;
	tmp[tot++] = '$';
	for(int i = 0; i < p; i++) {
		tmp[tot++] = '#';
		tmp[tot++] = s[i];
	}
	tmp[tot++] = '#';
}
long long manacher() {
	len[0] = 0;  
	int mx = -1;  
	int id = -1;
	long long res = 0;
	for(int i=0;i<tot;i++){  
	    if(i < mx) len[i] = min(mx - i, len[2 * id - i]);  
	    else len[i] = 1;  
	    while(i - len[i] >= 0 && i + len[i] < tot && tmp[i - len[i]]== tmp[i + len[i]]) len[i]++;  
	    if(len[i] + i > mx){  
	      mx = len[i] + i;  
	      id = i;  	
	    }  
	   // printf("%c %d\n",tmp[i],len[i]);
		res += len[i] / 2;
	}  
	return res;
}
int main() { 
	scanf("%d",&ca);
	while(ca--) {
		tot = 0;
		scanf("%s",s);
		scanf("%s",t);
		int p = strlen(s);
		if(!strcmp(s,t,0,p - 1)) {
			int l = -1,r = -1;
			for(int i = 0; i < p; i++) {
				if(s[i] != t[i]) {
					l = i;
					break;
				}
			}
			for(int i = p - 1; i >= 0; i--) {
				if(s[i] != t[i]) {
					r = i;
					break;
				}
			}
			if(!check(s,t,l,r)) {
				printf("%d\n",0);
			}
			else {
				int res = 0;
				while(l >= 0 && r < p) {
					if(s[l] == t[r] && t[l] == s[r]) {
						res++;
						l--;r++;
					}
					else break;
				}
				printf("%d\n",res);
			}
		}
		else {
			init(p);
			printf("%lld\n",manacher());
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41997978/article/details/89683243