Problem:
G: 回文部分列 (Palindromic Subsequences)

問題

英小文字のみからなる文字列 S が与えられるので、この文字列 S の連続とは限らない部分列であって、回文であるものは何種類あるかを求めてください。

ここで、S の連続とは限らない部分列とは、元の文字列 S から 1 文字以上 |S| 文字以下を任意に選択し (選択するそれぞれの文字の位置は非連続でも良い)、それらを元の順番通りに連結させてできた文字列のことを指します。この問題において、空文字列は部分列として認められないことに注意してください。

また、文字列 X が回文であるとは、元の文字列 X と、X を反転した文字列 X’ が等しいことを指します。

さらに、異なる部分列のとりかたの結果同じ回文が生成されたとしても、それは重複して数えないことに注意してください。例えば S = acpc である場合、 2 文字目のみからなる部分列と、4 文字目のみからなる部分列はどちらも回文 c ですが、これは複数回数えず、合わせて一度だけ数えることとします。

答えは非常に大きくなることがあるので、 1,000,000,007 で割った余りを出力してください。

入力形式

S

制約

1 \leq |S| \leq 2,000

S に含まれる文字は英小文字のみである

出力形式

答えを 1,000,000,007 で割った余りを 1 行で出力してください。

入力例1

acpc

出力例1

5

文字列 acpc の連続とは限らない部分列であって回文であるものは、 a, c, cc, cpc, p の 5 種類です。部分列の種類数を数えることに注意してください。

入力例2

z

出力例2

1

条件を満たす部分列は z のみです。空文字列は部分列として認められないことに注意してください。

入力例3

madokamagica

出力例3

28

Solution in C++11 (judge verdict: Accepted)

#include <bits/stdc++.h>
using namespace std;


typedef long long ll;
typedef pair<int,int> pii;
#define REP(i,n) for(int i=0;i<(int)(n);i++)
#define FOR(i,a,b) for(int i=(int)(a);i<(int)(b);i++)
#define FORR(i,a,b) for(int i=(int)(b)-1;i>=(int)(a);i--)

#define MOD 1000000007

int n;
char s[2521];
ll dp[2521][2521];

set<int> cs[26];

int main(){
  scanf("%s",s);
  n = strlen(s);
  REP(i,n)cs[s[i]-'a'].insert(i);

  dp[0][n] = 1;
  ll ans = 0;
  FORR(len,0,n+1)REP(l,n){
    int r = l+len;
    if(r>n)break;
    ll cnt = dp[l][r];
    if(cnt==0)continue;
    // step
    REP(i,26){
      auto it1 = cs[i].lower_bound(l);
      auto it2 = cs[i].lower_bound(r);
      if(it1==cs[i].end())continue;
      if(it2==cs[i].begin())continue;
      it2--;
      int x = *it1;
      int y = *it2;
      if(x>=y)continue;
      if(x>=r)continue;
      if(y<l)continue;
      ans += cnt;
      ans %= MOD;
      (dp[x+1][y] += cnt) %= MOD;
      // printf("%c(%d-%d)%c: %lld\n",'a'+i,x+1,y-1,'a'+i,cnt);
    }
    // finish
    REP(i,26){
      auto it = cs[i].lower_bound(l);
      if(it==cs[i].end())continue;
      int x = *it;
      if(x<r){
        // printf("%c single\n",'a'+i);
        ans += cnt;
        ans %= MOD;
      }
    }
  }
  printf("%lld\n",ans);
  return 0;
}