题目大意:
你有一些字符串,它们有各自出现的频率(百分数),分配这些频率使它们的Huffman编码和输入的一样,求可能的方案数。
我们先复习一下Huffman编码:
每次在所有元素中选择两个频率最小的,把它们合并成一个新的元素,其频率是两个频率的总和。新的元素的左子树是两个元素中小的那个,右子树是大的那个。左子树写0,右子树写1。最终拼出来的树,就是Huffman树。从树上往下走,走到的每一个根节点路径上所有的0\1拼起来,就是每个元素的Huffman编码。
更重要的是,Huffman树有一些性质:
对于相同深度的点,左边的频率小于等于右边的频率,这个很显然。这就够了,我们按从小到大的顺序填数,如果是根节点直接填,否则可以算出这个点的频率。加上可行性剪枝,就A掉啦(跑得好慢)
#include<bits/stdc++.h>
#define N 50100
using namespace std;
char sss[N],a[5],b[5];
vector<int> cx[531442];
int len;
inline int gethas(char *c){
int tot=0;
for(int i=0;i<strlen(c);i++) tot=tot*27+c[i]-'a'+1;
return tot;
}
inline void check_min(int &a,int b){if(b<a) a=b;}
map<long long,int> s;
long long base=27*27*27*27;
int main(){
scanf("%s",sss);len=strlen(sss);
for(int i=0;i<len;i++){
int has=0;
for(int j=0;j+i<len&&j<4;j++){
has=has*27+sss[i+j]-'a'+1;
cx[has].push_back(i);
}
}
int q;scanf("%d",&q);
while(q--){
scanf("%s%s",a,b);
int hasa=gethas(a),hasb=gethas(b);
int ll=strlen(a),rr=strlen(b);
if(hasa>hasb) swap(hasa,hasb),swap(ll,rr);
int l=cx[hasa].size(),r=cx[hasb].size();
int ans=1000086;
if(l==0||r==0) puts("-1");
else{
if(s[hasa*base+hasb]){
printf("%d\n",s[hasa*base+hasb]);
continue;
}
for(int i=0,now=0;i<l;i++){
while(cx[hasb][now]<cx[hasa][i]&&now<r) now++;
if(now!=r) ans=min(ans,(int)max(cx[hasb][now]+rr,cx[hasa][i]+ll)-cx[hasa][i]);
if(now!=0) ans=min(ans,(int)max(cx[hasb][now-1]+rr,cx[hasa][i]+ll)-cx[hasb][now-1]);
}
printf("%d\n",s[hasa*base+hasb]=ans);
}
}
return 0;
}