You've got a string s = s1s2... s|s| of length |s|, consisting of lowercase English letters. There also are q queries, each query is described by two integers li, ri (1 ≤ li ≤ ri ≤ |s|). The answer to the query is the number of substrings of string s[li... ri], which are palindromes.
String s[l... r] = slsl + 1... sr (1 ≤ l ≤ r ≤ |s|) is a substring of string s = s1s2... s|s|.
String t is called a palindrome, if it reads the same from left to right and from right to left. Formally, if t = t1t2... t|t| = t|t|t|t| - 1... t1.
The first line contains string s (1 ≤ |s| ≤ 5000). The second line contains a single integer q (1 ≤ q ≤ 106) — the number of queries. Next q lines contain the queries. The i-th of these lines contains two space-separated integers li, ri (1 ≤ li ≤ ri ≤ |s|) — the description of the i-th query.
It is guaranteed that the given string consists only of lowercase English letters.
Print q integers — the answers to the queries. Print the answers in the order, in which the queries are given in the input. Separate the printed numbers by whitespaces.
caaaba 5 1 1 1 4 2 3 4 6 4 5
1 7 3 4 2
Consider the fourth query in the first test case. String s[4... 6] = «aba». Its palindrome substrings are: «a», «b», «a», «aba».
题目大意:
给一个字符串,再给一个查询次数n,和n个查询区间,问在区间内一共有多少个回文序列
解题思路&反思:
(1)对于长度为1 的子序列,回文数一定是1
(2)长度为2 的序列,如果本身是回文,则回文数就是3,否则是2
(3)在往上的长度的序列,可以通过递推关系:
dp[i][j]=dp[i][j-1]+dp[i+1][j]-dp[i+1][j-1];
如果[i,j]本身是回文,则还需要再+1,也就是
if(ifp[i][i+l]) dp[i][i+l]+=1;
由此就可以通过记忆化搜索或者递推求出所有区间的回文数,再离线查询即可。
此外,今天第一次遭遇了卡cin和cout还有endl的情况,不管是记忆化搜索还是递推,改了之后就都过了。
以后还是尽量用scanf和printf写。
这是递推的代码:
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <memory.h>
#include <string>
#include <vector>
#include <list>
#include <map>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <numeric>
#include <functional>
#define maxn 5005
using namespace std;
bool ifp[maxn][maxn];
int dp[maxn][maxn];
char s[maxn];
int main()
{
gets(s);
int lenth=strlen(s);
int n;
scanf("%d",&n);
for(int i=0;i<lenth;i+=1){
dp[i][i]=1;
ifp[i][i]=true;
}
for(int l=1;l<lenth;l+=1){
for(int i=0;i+l<lenth;i+=1){
if(l==1){
if(s[i]==s[i+l]) {
dp[i][i+l]=3;
ifp[i][i+l]=true;
}
else dp[i][i+l]=2;
}
else{
ifp[i][i+l]=ifp[i+1][i+l-1]&&(s[i]==s[i+l]);
dp[i][i+l]=dp[i][i+l-1]+dp[i+1][i+l]-dp[i+1][i+l-1];
if(ifp[i][i+l]) dp[i][i+l]+=1;
}
}
}
for(int i=0;i<n;i+=1){
int l,r;
scanf("%d %d",&l,&r);
printf("%d\n",dp[l-1][r-1]);
}
return 0;
}
这是记忆化搜索的代码:
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <memory.h>
#include <string>
#include <vector>
#include <list>
#include <map>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <numeric>
#include <functional>
#define maxn 5005
using namespace std;
typedef __int64 ll;
int ifp[maxn][maxn];
int dp[maxn][maxn];
char s[maxn];
int ifpalin(int l,int r)
{
if(ifp[l][r]!=-1) return ifp[l][r];
else if(l==r) return ifp[l][r]=1;
else if(l==r-1&&s[l]==s[r]) return ifp[l][r]=1;
else if(s[l]!=s[r]) return ifp[l][r]=0;
else return ifp[l][r]=ifpalin(l+1,r-1);
}
int solve(int l,int r)
{
if(dp[l][r]!=-1) return dp[l][r];
else if(l==r) return dp[l][r]=1;
else if(l==r-1){
if(ifpalin(l,r)) return dp[l][r]=3;
else return dp[l][r]=2;
}
else{
dp[l][r]=solve(l,r-1)+solve(l+1,r)-solve(l+1,r-1);
if(ifpalin(l,r)) dp[l][r]+=1;
return dp[l][r];
}
}
int main()
{
for(int i=0;i<maxn;i+=1){
for(int j=0;j<maxn;j+=1){
dp[i][j]=-1;
ifp[i][j]=-1;
}
}
gets(s);
int n;
scanf("%d",&n);
for(int i=0;i<n;i+=1){
int l,r;
scanf("%d %d",&l,&r);
printf("%d\n",solve(l-1,r-1));
}
return 0;
}