原题地址:点击打开链接
今天我要发表一篇為什麼很多人都比较贱的演讲,因为有的人就是要到快走投无路或山穷水尽才懂得忽然拼命干正事,就像我。通常经常这样做的结果有两种——1 就是短命+每次拼都成功了;2 就是短命+不是每次都成功了。 暂时我是属于第一种,但是即将又有另外一个挑战,我觉得我真的比较贱,因为这样的生活我觉得不是正常的,我还要照顾妹子,我怎么能让身体经常受这样的折腾?所以这一次无论怎么样,我必须要平时要多下把功夫,希望应对各种挑战时投入精力的曲线能比较平滑,不要那么陡峭。
现在每天的计划非常重,杀手哥叶大神笑话我怎么来到北京还要做宅男。
每天看PAPER,实在不喜欢,我还是要敲程序的人,我TM就是去当工程师的……希望川神能顺利推我,我也能顺利通过。扯远了,下面是正文……
——————正文——————
(PS:后来发现这题有更好的解法,迟一点找个时间更新……现在还没更新)
这题看了很久,但是是在前几天,应该说是在MSRA入职后的第2天,才想到一个比较好的方法,而且讽刺的是我还是从我看的paper里面获得idea的……
思路背景:通常,给定一篇文章,我们知道里面有许多的单词,那么我们对于一个单词t在什么地方出现,可以使用一串整数来表示,例如 t: 1 3 7 10 21,就表示单词t出现在文章中的第1,3,7,10,21个单词的位置。
好了,现在就把这种思想应用到这一题,我以本题的测试样例举例。很显然,按照我刚才的方法,有如下排列:
hot : 0 4
dog: 1 2
milk: 3
其实,我们只要找出题目所说的文章中有哪些要背的单词,都可以列出上面这个表,列出上面这个表有什么用?犀利咯!你有没有发现,任意从每一行选一个数字,然后用最大的减最小的再加1,就是肯定包含了这几个单词的一段文章的长度?现在的问题就是,你用什么策略去选择这些数,或者不说选择,而是检查适合的数字们是什么。
我们首先建立一个小根堆,将每个单词开头的数字0,1,3入堆的同时,检查得到当前的最小长度是(3-0+1)=4 。 这个时候,我们我们将堆的最小元素出堆,同时检查到该元素是属于hot的,那么我们将hot的下一个元素4入堆,然后当前堆中的元素就是1,3,4,此时长度是(4-1+1)=4,没有变化,然后此时最小元素是1,出堆,同时发现1是属于dog的,而dog后面还有元素,于是将2入堆,现在堆元素为2,3,4,此时长度是(4-2+1)=3,更新当前的最小距离为3。然后我们将元素2出堆,元素2是属于dog的,但是2后面已经没有元素,我们结束计算,于是就得到最小距离3.
為什麼这个算法是正确的?要注意到,每次堆中的所有元素(实际是某个单词对应的下标),肯定覆盖了所有文章中出现的要背的单词,所以我们只需要计算堆中的最大元素和最小元素的差,再加1,就可以得到一个长度,这个范围里肯定包含了所有的单词。為什麼每次都要将最小的元素出堆,然后挑选其下一个元素?因为只有这样,才有可能使得最小长度变小,如果出堆的不是最小的元素,那么再次进入堆中的元素肯定是大于当前堆的最小元素的,因此再计算最小长度,肯定是不行的,这也解释了為什麼当发现出堆的元素后面已经没有元素时,就可以结束算法。
我解释得不太好,但是基本思路就是这样,至于其它的方面,就是将单词映射为数字,例如hot是1,dog是2,milk是3,然后用一个vector v[4],v[1][0]就表示hot的第一个出现位置这样的,需要用到map,不过就用那么一小下,在建立好上面那个表后就不需要用了。其次是堆中存储的元素的数据结构,里面包含了下标,包含了当前下标对应的单词编号,以及当前下标在表中对应的下标是多少,例如对于0,就是存储了{0,1,0},对于2就是存储了{1,2,1}。我这么说可能更加乱……按照自己的思路写就好,毕竟最关键只是基本算法,细枝末节什么的,能实现就好。
这题需要注意的地方是,当文章中没有出现要背的单词,那么输出两个0就可以了,我因为这个runtime error了一次。
下面就贴代码了,我写得非常丑啊,而且堆这个数据结构也是手敲的,所以代码小长,但是结构还是比较清晰的。各位也可以使用STL中内置的优先队列,更加快更加短就能解出这题,只是我想写一下堆。果不其然,我真的写错了一次,后来又去翻算法导论这本书……
代码如下(VIM写的代码贴到这里全走样了):
#include<iostream>
#include<stdio.h>
#include<vector>
#include<string>
#include<algorithm>
#include<cmath>
#include<map>
#define MAX 0x7fffffff
#define MM 100005
#define MN 1005
using namespace std;
int n,m;
string s;
struct node
{
int index,p,mark;
node(){}
node(int ii, int pp, int mm){
index=ii;
p=pp;
mark=mm;
}
}heap[2*MM];
int max_index;
int ans;
vector<node> v[MN];
int heap_size;
int l,r;
void enHeap(node &e)
{
max_index = max(max_index,e.index);
heap[++heap_size]=e;
int cur=heap_size;
while(cur>1)
{
if(heap[cur].index<heap[cur/2].index)
{
swap(heap[cur],heap[cur/2]);
cur=cur/2;
}
else
break;
}
}
void popHeap()
{
int p=heap[1].p;
int mm=heap[1].mark+1;
if(mm==v[p].size())
{
heap_size--;
return;
}
heap[1]=v[p][mm];
max_index=max(heap[1].index,max_index);
int cur=1;
int smallest,l,r;
while(cur*2<=heap_size)
{
smallest=cur;
l=cur*2;
r=cur*2+1;
if(heap[l].index<heap[smallest].index)
{
smallest=l;
}
if(r<=heap_size && heap[r].index<heap[smallest].index)
{
smallest=r;
}
if(smallest==cur)
break;
swap(heap[cur],heap[smallest]);
cur=smallest;
}
}
int main()
{
//freopen("in","r",stdin);
//freopen("out","w",stdout);
map<string, int> mp;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
cin>>s;
mp[s]=i+1;
}
int tmp;
scanf("%d",&m);
for(int i=0;i<m;i++)
{
cin>>s;
if(tmp=mp[s])
v[tmp].push_back(node(i,tmp,v[tmp].size()));
}
int cnt=0;
for(int i=0;i<=n;i++)
{
if(v[i].size())
{
cnt++;
if(heap_size)
enHeap(v[i][0]);
else
heap[++heap_size]=v[i][0];
}
}
if(cnt==0)
{
printf("0\n0\n");
return 0;
}
ans=MAX;
while(heap_size==cnt)
{
ans=min(ans, max_index-heap[1].index+1);
popHeap();
}
printf("%d\n%d\n",cnt,ans);
}
转载请注明:http://blog.youkuaiyun.com/fanfank