第五章 字串專題 ---------------- 5.11 題解:最短摘要的生成
題目:
Alibaba筆試題,給定一段產品的英文描述,包含M個英文單詞,每個英文單詞以空格分隔,無其他標點符號;再給定N個英文單詞關鍵字,請說明思路並程式設計實現方法。String extractSummary(String description,String[] key words)目標是找出此產品描述中包含N個關鍵字(每個關鍵詞至少出現一次)的長度最短的子串,作為產品簡介輸出。(不限程式語言)20分。
思路一:
直接暴力解法,迴圈暴力破解,每次判斷關鍵字是否全部包含,然後不斷更新邊界。
思路二:
先來看看這些序列(w為英文單詞,q為關鍵字):
w0,w1,w2,w3,q0,w4,w5,q1,w6,w7,w8,q0,w9,q1
問題在於,一次性掃描確定邊界這不太現實,肯定要多次掃描,不斷更新邊界,這裡只說每個關鍵字必須要出現一次以上,並沒有說要順序出現。先第一次掃描,掃描到第一個關鍵字,從第一個位置w0處將掃描到q0處:
w0,w1,w2,w3,q0,w4,w5,q1,w6,w7,w8,q0,w9,q1
然後再從q0處開始掃描,直到出現第一個包含所有關鍵字的序列,並記錄邊界:
w0,w1,w2,w3,q0,w4,w5,q1,w6,w7,w8,q0,w9,q1
然後掃描的位置繼續往後移動,移動到q1,這樣包含的序列中就少了q0,那麼就從q1開始掃描,這樣就可以找到下一個包含所有關鍵字的序列,即從q1掃描到了q0處,這兒也要更新邊界:
w0,w1,w2,w3,q0,w4,w5,q1,w6,w7,w8,q0,w9,q1
然後,在繼續往後掃描,定位到下一個關鍵字q0,繼續往後掃描,如此迴圈往後掃描,直至掃描完成,那麼邊界長度就是最小的了。
程式碼:
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class 最短摘要 {
public static void main(String[] args) {
solve1(new String[]{"a", "b", "c", "seed", "h", "e", "f", "c", "c", "seed", "e", "f", "seed", "c"},
new String[]{"c", "e"});
solve2(new String[]{"a", "b", "c", "seed", "c", "e", "f", "c", "c", "seed", "e", "f", "seed", "c"},
new String[]{"c", "c", "e", "f"});
solve2(new String[]{"a", "b", "a", "a", "b", "c", "d", "h", "e", "f", "f"}, new String[]{"b", "c", "d"});
}
// 暴力破解法
public static void solve1(String[] w, String[] q) {
int length = Integer.MAX_VALUE;
int begin = -1;
int end = -1;
for (int i = 0; i < w.length; i++) {
// 求以i開頭包含所有關鍵字的序列
for (int j = i+1; j < w.length; j++) {
// 如果全部關鍵字已經再seq中。
if (containsAll(q,w,i,j)) {
// 判斷當前這個序列是不是較短的序列
// System.out.println(seq);
if (j-i+1<length) {
length = j-i+1;
begin = i;
end = j;
}
break;
}
}
}
print(w,begin,end);
}
/**
* 這種解法解決了keys關鍵字中有重複的現象
* @param w
* @param keys
*/
public static void solve2(String[] w, String[] keys) {
Arrays.sort(keys);
// begin和end用於在找到更短的包含全部關鍵字的子陣列時更新
int begin = -1;
int end = -1;
int j = -1; // 上一次囊括了所有關鍵字的右邊界
int minLen = Integer.MAX_VALUE;
for (int i = 0; i < w.length; i++) {
// 如果i位置是關鍵字,求以i開頭包含所有關鍵字的序列
String word1 = w[i];
int index = Arrays.binarySearch(keys, word1);
if (-1==index) {
continue;
}else { // i是一個關鍵字
// 如果已經全部找到
if (j<w.length&&j>=i&&containsAll(keys, w, i, j)) {
if (j-i+1<minLen) { // 更新
minLen = j-i+1;
begin = i;
end = j;
}
continue;
}
}
if (j==-1) {
j = j+1; // j值初始化
}
while(j<w.length){
String word2 = w[j]; // 文章單詞
int index1 = Arrays.binarySearch(keys, word2);
if (-1==index1) {
j++;
continue;
}else { // 找到關鍵字 這裡應該是第一次掃描的包含所有關鍵字的 然後繼續for迴圈不斷更新邊界
if (containsAll(keys, w, i, j)) { //全部到齊
if (j - i + 1 < minLen) {// 更新
minLen = j - i + 1;
begin = i;
end = j;
}
break;
}else {
j++;
}
}
}
}
print(w, begin, end);
}
private static boolean containsAll(String[] keywords, String[] w, int i, int j) {
Map<String, Integer> map = new HashMap<>();
for (int k = 0; k < keywords.length; k++) {
String key = keywords[k];
if (map.get(key)==null) {
map.put(key, 1);
}else {
map.put(key, map.get(key)+1);
}
}
Map<String, Integer> map2 = new HashMap<>();
for (int k = i; k <= j; k++) {
String key = w[k];
if (map2.get(key)==null) {
map2.put(key, 1);
}else {
map2.put(key, map2.get(key)+1);
}
}
for(Map.Entry<String, Integer> e:map.entrySet()){
if (map2.get(e.getKey())==null||map2.get(e.getKey())<e.getValue()) {
return false;
}
}
return true;
}
private static void print(String[] w, int begin, int end) {
System.out.println(begin+" "+end);
for (int i = begin; i <=end; i++) {
System.out.print(w[i]+" ");
}
System.out.println();
}
}
結果:
C++程式碼:(沒除錯成功=。=)
#include<iostream>
#include<algorithm>
#include<climits>
#include<map>
using namespace std;
template<class T>
int length(T& arr)
{
return sizeof(arr)/sizeof(arr[0]);
}
bool containAll(string keywords[],string w[],int i,int j,int kl)
{
map<string,int> mp;
//len儲存keywords長度,避免直接用於for迴圈時,多次呼叫,複雜度提高
int len=kl;
for(int k=0;k<len;k++)
{
string key=keywords[k];
if(mp.find(key)==mp.end())
{
mp[key]=1;
}
else
{
mp[key]++;
}
}
map<string,int> mp2;
for(int k=i;k<=j;k++)
{
string key=w[k];
if(mp2.find(key)==mp2.end())
{
mp2[key]=1;
}
else
{
mp2[key]+=1;
}
}
for(map<string,int>::iterator it=mp.begin();it!=mp.end();it++)
{
if(mp2.find(it->first)==mp2.end()||mp2[it->first]<mp[it->first])
{
return false;
}
}
//對於mp中的所有元素,在mp2中都能找到,且value值不少於mp中的value值,則返回true
return true;
}
int strSearch(string s[],string str,int kl)
{
int l=kl;
for(int i=0;i<l;i++)
{
if(s[i]==str) return 1;
}
return 0;
}
//w:要檢索的字串陣列; key:要找到關鍵字
void solve2(string w[],string keys[],int kl,int wl)
{
int kLen=kl;
int wLen=wl;
sort(keys,keys+kLen);
//begin和end用於在找到更短的包含全部關鍵字的子陣列時更新
int begin=-1;
int end=-1;
int j=-1;//上一次囊括了所有關鍵字的右邊界
int minLen=INT_MAX;
for(int i=0;i<wLen;i++)
{
// 如果i位置是關鍵字,求以i開頭包含所有關鍵字的序列
string word1=w[i];
//如果在keys中沒找到w[i],即,w[i]不是關鍵字,則continue
if(!strSearch(keys,word1,kl))
{
continue;
}
else
{
//i是一個關鍵字
//如果已經全部找到
if(j<wLen&&j>=i&&containAll(keys,w,i,j,kl))
{
if(j-i+1<minLen)
{ //更新
minLen=j-i+1;
begin=i;
end=j;
}
continue;
}
}
if(j==-1)
{
j=i+1;//j值初始化
}
while(j<wLen)
{
string word2=w[j];
//word2不是關鍵字
if(!strSearch(keys,word2,kl))
{
j++;
continue;
}
// word2是關鍵字
else
{
//全部到齊
if(containAll(keys,w,i,j,kl))
{
if(j-i+1<minLen)
{
minLen=j-i+1;
begin=i;
end=j;
}
break;
}
else
{
j++;
}
}
}
}
//輸出最後結果
for(int i=begin;i<=end;i++)
{
cout<<w[i]<<" ";
}
}
int main()
{
string ww[]={"a", "b", "c", "seed", "h", "e", "f", "c", "c", "seed", "e", "f", "seed", "c"};
string ky[]={"c", "e"};
int wl=length(ww);
int kl=length(ky);
cout<<wl;
cout<<kl;
solve2(ww,ky,wl,kl);
return 0;
}
相關文章
- 第五章 字串專題 ---------------- 5.5 題解:壓縮字串字串
- 第五章 字串專題 ---------------- 5.2 題解:巧妙翻轉字串字串
- 第五章 字串專題 ---------------- 5.7 題解:旋轉詞字串
- 第五章 字串專題 ---------------- 5.10 題解:神奇的迴文串字串
- 第五章 字串專題 ---------------- 5.8 題解:將字串中按單詞翻轉字串
- 第五章 字串專題 ---------------- 5.1 題解:判斷字串有無重複字元字串字元
- 第五章 字串專題 ---------------- 5.6 解題:判斷兩字串的字符集是否相同字串
- 第五章 字串專題 ---------------- 5.9 題解:去掉字串中連線出現的k次的0字串
- 第五章 字串專題 ---------------- 5.12 字串匹配之PabinKarp字串匹配
- 第五章 字串專題 ---------------- 字串匹配(二)----KMP演算法字串匹配KMP演算法
- 第五章 字串專題 ---------------- 5.4 實踐:替換字串中的空格字串
- [題解]P1641 生成字串字串
- 第五章 字串專題 ---------------- 字串匹配(三)----字尾陣列演算法字串匹配陣列演算法
- 單/全最短路專題 兩題
- 第五章 字串專題 ---------------- 5.3 走出思維誤區:變形詞問題字串
- 字串專題字串
- 專題:字串字串
- 2020複習專題——最短路
- Day1 最短路專題
- 【專題訓練】字串字串
- 提高組字串專題1字串
- P3735 字串 題解字串
- 最短路徑問題
- Day3 生成樹專題
- [JLOI2015] 有意義的字串 題解字串
- Luogu P10869 LCMs 題解 [ 黃 ] [ lcm ] [ 最短路 ]
- bellman-ford 單源最短路問題 圖解圖解
- 單源最短路問題
- 第五章練習題
- 專題六 最小生成樹【Kuangbin】
- CF618F Double Knapsack 題解(最短程式碼)
- 普利姆演算法解決最短修路問題演算法
- 使用A*演算法解迷宮最短路徑問題演算法
- PTA搜尋專題部分題解
- 第五章練習題2
- 第五章練習題3
- 第五章練習題5
- 14.第五章模態命題練習題