题意就不说了,我们设一个dp数组,dp[i]表示长度为i的前缀有多少种符合条件的分割方法,dp[0]设置为1。
那么dp[i]=dp[i-1]+dp[i-2]+...+dp[i-j],j是当前第i个字符和第i-j+1个字符之间所包含字符对应的限制数组(题目中的ai)的最小值。所以在计算的时候,要不断更新 j 的值保证符合条件。
O(n^2)的时间复杂度算出dp[n]就是最多方案数。最长子串在dp的过程中更新。借助一个数组lastMatch求分割的最少数量,lastMatch[i]表示以第i个字符开始,最长到lastMatch[i]个字符为一段,最后贪心,O(n)跑一遍求出最少分割数。
先用python写了一遍 TLE ,以为是语言的事,用C++写了一遍还是TLE,才发现是有数据死循环了。最后都过了,python 900MS / C++ 15MS。
#python 900MS
from __future__ import print_function;
n=input();
str=raw_input();
arr=raw_input().split();
arr=map(eval,arr);
MOD=1e9+7;
dp=[0]*3200;
dp[0]=1;
lastMatch=[0]*3200;
for i in range(len(lastMatch)) :
lastMatch[i]=i;
maxSubLen=0;
for i in range(1,len(str)+1) :
j=1;
k=arr[ord(str[i-1])-ord('a')];
while j<=k and i-j>=0 :
maxSubLen=max(j,maxSubLen);
dp[i]=(dp[i]+dp[i-j])%MOD;
t=i-j-1;
lastMatch[t+1]=max(lastMatch[t+1],i-1);
t=t if t>0 else 0;
k=min(k,arr[ord(str[t])-ord('a')]);
j+=1;
lastIndex=0;
lastStrIndex=len(str)-1;
subNum=0;
#print(lastMatch[0:10]);
while lastIndex<=lastStrIndex :
#print('last ',lastIndex);
subNum+=1;
lastIndex=lastMatch[lastIndex];
lastIndex+=1;
print(int(dp[len(str)]),maxSubLen,subNum,sep='\n');
//C++ 15MS
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#define maxn 3200
const int mod=1e9+7;
using namespace std;
int dp[maxn],lastMatch[maxn];
int arr[30];
int main()
{
int n;
string str;
cin>>n;
cin>>str;
for(int i=0;i<26;++i)
{
cin>>arr[i];
}
memset(dp,0,sizeof(dp));
dp[0]=1;
for(int i=0;i<str.size();++i)
lastMatch[i]=i;
int maxSubLen=0;
for(int i=1;i<=str.size();++i)
{
int j=1;
int k=arr[str[i-1]-'a'];
while(j<=k && i-j>=0)
{
maxSubLen=max(j,maxSubLen);
dp[i]=(dp[i]+dp[i-j])%mod;
int t=i-j-1;
lastMatch[t+1]=max(lastMatch[t+1],i-1);
t = t > 0 ? t : 0;
k = min(k,arr[str[t]-'a']);
j++;
}
}
int lastIndex=0;
int lastStrIndex=str.size()-1;
int subNum=0;
while(lastIndex<=lastStrIndex)
{
subNum+=1;
lastIndex=lastMatch[lastIndex];
lastIndex++;
}
printf("%d\n%d\n%d\n",dp[str.size()],maxSubLen,subNum);
}