别有洞天的尺取法——最短含关键词子串问题

短含关键词子串问题

尺取法

最短摘要生成

/*
Alibaba笔试题,给定一段产品的英文描述,包含M个英文单词,每个英文单词以空格分隔,

无其他标点符号;再给定N个英文单词关键字,请说明思路并编程实现方法。
String extractSummary(String description,String[] key words)
目标是找出此产品描述中包含N个关键字(每个关键词至少出现一次)的长度最短的子串,
作为产品简介输出。
 */
/*
 * 尺取法的思想是:
 * 两个指针一遍向右扫描 不断更新包含所有关键字的范围  具体操作是:
 * 从第一个单词开始扫描,直到遇到第一个关键词就先停下来并记录下标作为起点
 * 然后终点下标从后面开始扫描,遇到已经扫描过的关键词则不计数,继续向后直至包含所有关键词,
 * 记录终点下标以及这一段子串的长度,这样就确定了一个包含所有关键词的子串范围。
 * 然后起点向后移动,终点不变,如果还包含所有关键字就记录起点和终点下标,
 * 直到不能包含所有关键字时,终点才向后移动。又直到包含所有关键词时记录起点和终点下标。
 * 如此循环至遍历完该英文描述 
 * 
 */
package 字符串;

import java.util.Arrays;

public class 最短摘要生成_尺取法 {

	public static void extractSummary(String[] description,String[] keys) {
		
		//begin和end用于记录找到更短合法子串的起始和终点下标
		int begin = -1;
		int end  = -1;
		//扫描指针
		int sc = -1;
		
		int minLen=Integer.MAX_VALUE;
		int[] found=new int[keys.length];
		
		for(int i=0;i<description.length;i++) {
			//每次确定范围 foud数组都要清空为0
			Arrays.fill(found, 0);
			
			String word=description[i];
			int index=indexOf(keys,word);
			if(index==-1) {
				continue;
			}
			else{
				found[index]=1;
			}
			
			int j;
			
			//第一轮时 j在i的后一个位置 之后j的值从上一轮的终点下标开始
			if(sc==-1)
				j=i+1;
			else {
				j=sc;
			}
			for(;j<description.length;j++) {
				String nextWord=description[j];
				int nextIndex=indexOf(keys,nextWord);
				if(nextIndex==-1||found[nextIndex]==1) {
					//如果不是关键字 或者该关键字在本轮已经出现过了则跳过
					continue;
				}
				else {
					found[nextIndex]=1;
					
					//如果全部关键字都找齐 记录终点下标
					if(sum(found)==keys.length) {
						//记录这轮终点位置
						sc=j;
						//遇到更短的则更新并记录起点终点下标
						if(j-i+1<minLen) {
							minLen=j-i+1;
							begin=i;
							end=j;
						}
						break;
					}
					
				}
			}
		}
		show(description,begin,end);
	}
	//判断该单词是否在给定关键词范围 由则返回下标无则返回-1
	private static int indexOf(String[] keys, String word) {
		for(int i=0;i<keys.length;i++) {
			//keys[i]==word
			if(keys[i].equals(word))
				return i;
		}
		return -1;
	}
	private static void show(String[] description, int begin, int end) {
		//若描述串不含有所有关键字的子串 
		if(begin==-1||end==-1) {
			System.out.println("无满足要求子串");
			System.exit(0);
		}
		for (int i =begin; i <= end; i++) {
			System.out.print(description[i]+" ");
		}
	}
	private static int sum(int[] found) {
		int res=0;
		for (int i = 0; i < found.length; i++) {
			res+=found[i];
		}
		return res;
	}
	public static void main(String[] args) {
		String[] words = {"a","b","c","d","a","a","b","c","t"};
		String[] keys ={"a","b","c"};
		extractSummary(words,keys);
		String[] words1 = {"a","b","d","c","a","a","b","c","t"};
		String[] keys1 ={"ab","b","c"};
		extractSummary(words1,keys1);
		
		
	}

}

hiho字符串

/*
 *描述


如果一个字符串恰好包含2个’h’、1个’i’和1个’o’,我们就称这个字符串是hiho字符串。

例如"oihateher"、"hugeinputhugeoutput"都是hiho字符串。

现在给定一个只包含小写字母的字符串S,小Hi想知道S的所有子串中,最短的hiho字符串是哪个。

输入
字符串S

对于80%的数据,S的长度不超过1000

对于100%的数据,S的长度不超过100000

输出
找到S的所有子串中,最短的hiho字符串是哪个,输出该子串的长度。如果S的子串中没有hiho字符串,输出-1。

样例输入
happyhahaiohell
样例输出
5
 
 */
 
/*
 * 这题与最短生成摘要类似 不过要注意 恰好包含2个’h’、1个’i’和1个’o’
 * 所以是最短包含关键词不重复子串 要注意最后在最短生成摘要基础上判别关键词个数
 * 
 */
package 字符串;
import java.util.Scanner;

public class hiho字符串_尺取法2 {

	public static void main(String[] args) {
		Scanner reader=new Scanner(System.in);
		String s=reader.nextLine();
		char[] word=s.toCharArray();
		System.out.println(hiho(word));

	}

	private static int hiho(char[] word) {
		int minLen=Integer.MAX_VALUE;
		int j=-1;
		int end=-1;
		int begin=-1;
		for(int i=0;i<word.length;i++) {
			char c=word[i];
			if(check(c)) {
				if(j==-1) {
					j=i+1;
				}
				for(;j<word.length;j++) {
					char nextC=word[j];
					if(check(nextC)&&containAll(word,i,j)) {
						if(check(word,i,j)&&j-i+1<minLen) {
							minLen=j-i+1;
							begin=i;
							end = j;
						}
						break;
					}
				}
			}
		}
		show(word,begin,end);
		return (minLen==Integer.MAX_VALUE?-1:minLen);
	}

	private static boolean check(char[] word, int i, int j) {
		int c1=0,c2=0,c3=0;
		for(int k=i;k<=j;k++) {
			if(word[k]=='h') c1++;
			if(word[k]=='i') c2++;
			if(word[k]=='o') c3++;
		}
		return c1==2&&c2==1&&c3==1;
	}

	private static boolean containAll(char[] word, int i, int j) {
		int c1=0,c2=0,c3=0;
		for(int k=i;k<=j;k++) {
			if(word[k]=='h') c1++;
			if(word[k]=='i') c2++;
			if(word[k]=='o') c3++;
		}
		return c1>=2&&c2>=1&&c3>=1;
	}

	private static boolean check(char c) {
		return c=='h'||c=='i'||c=='o';
	}
	private static void show(char [] a,int begin, int end) {
		//若描述串不含有所有关键字的子串 
		if(begin==-1||end==-1) {
			System.out.println("无满足要求子串");
			System.exit(0);
		}
		for (int i =begin; i <= end; i++) {
			System.out.print(a[i]+" ");
		}
	}

}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值