UVALive-3716 DNA Regions
题目大意:
有两个字符串, 现在要你选择一个区间[ l , r ], 要求这个区间内部两个串不相同的位置小于等于p%, 求这个区间最长是多长.
设dp[ i ]表示以i为结尾的满足条件的最长串长度是多少.
然后我们观察一下转移条件, 我们可以发现, 需要满足的条件是(sum[ i ]-sum[ j ])/( i-j )<=p/100, 其中sum表示不相同的位置的前缀和.
日常化式子就可以得到sum[ i ]*100-p*i<=sum[ j ]*100-p*j, 所以我们需要的就是满足条件的最小的j.
那么我们把这个值记为f, 丢到一个数组( 其实这个数组就像是一个单调栈, 但是单调性维护不一样 )里面.
如果当前i的f[ i ]大于数组最后一个( 可以理解为栈顶 ), 就直接把j压进去, 因为不存在f[ j ]比f[ i ]大了,这样就保证了这个数组( 单调栈 )是按f递增的;
如果小于最后一个, 就在存的这个数组中二分查找大于f[ i ]的最小的f[ j ].
由于我们放进去是从前往后放的, 所以数组中的f[ j ]对应的j也是递增的, 选择最小的f[ j ]就是最小的j, 此时长度也是最长的, 然后dp[ i ]=i-j.
查找了就不用再压到数组里面去了.
因为后面的是查找最小的j满足f[ i ]<=f[ j ], 而当前i进行二分查找就说明数组中存了比当前f[ i ]大的f[ j ], 且这个j在当前i前面, 如果后面有一个i'满足f[ i' ]<=f[ i ], 那么也满足f[ i' ]<=f[ j ], 且i和i'的距离要比j和i'的距离远, 所以直接弃掉当前的f[ i ]没问题.
其实有许多东西都是可以不用开的, 比方说sum[ i ]和dp[ i ], 直接用变量记下来就可以了.
实时对ans取min.
代码如下:
//made by Crazy01
#include<bits/stdc++.h>
#define inf 1<<30
#define ll long long
#define db double
#define c233 cout<<"233"<<endl
#define mem(s) memset(s,0,sizeof(s))
const int N=150050;
using namespace std;
char a[N],b[N];
int f[N];
int n,p,ans,sum;
struct lll{
int q[N];
int tp;
void clear(){q[0]=0; tp=0;}
bool empty(){return tp==0;}
void push(int x){q[++tp]=x;}
bool ck(int x){return f[q[tp]]<f[x];}
int lyb(int x){
int l=0,r=tp;
while(l<=r){
int mid=(l+r)>>1;
if(f[q[mid]]<f[x])l=mid+1;
else r=mid-1;
}
return q[l];
}
}q;
inline int gi(){
int x=0,res=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
while(ch<='9'&&ch>='0')x=(x<<1)+(x<<3)+ch-48,ch=getchar();
return x*res;
}
void init(){
p=gi();
scanf("%s",a+1);
scanf("%s",b+1);
}
void work(){
ans=sum=0;
for(int i=1;i<=n;i++){
sum+=(a[i]!=b[i]);
f[i]=100*sum-p*i;
if(q.ck(i)){q.push(i);continue;}
ans=max(ans,i-q.lyb(i));
}
if(ans)printf("%d\n",ans);
else printf("No solution.\n");
}
int main(){
n=gi();
while(n){
q.clear();
init();
work();
n=gi();
}
return 0;
}