在面试100题中看到了这个题,实现下:
题目:给一个很长的字符串str ,还有一个字符集比如"abc" ,找出str 里包含"abc"的最短子串。要求O(n)。
例如:字符串str:abdcaabcx,字符集dest:abc,那么最短子串就是abc。
实现思路是在july大神的博客上看到的http://blog.youkuaiyun.com/v_july_v/article/details/6126444
用两个变量 front,rear 指向一个的子串区间的头和尾(当然,开始时front和rear都指向字符串开始处)。
用一个int cnt[255]={0}记录当前这个子串里字符集a,b,c各自的个数,一个变量count记录字符集里有多少个了。
rear 一直加,更新cnt[]和count的值,直到count等于字符集个数。
然后front++,直到cnt[]里某个字符个数为0(front 开始的部分有可能和后面的重复,所以front要加到某个字符个数为0), 这样就找到一个符合条件的字串了,继续下去,可以求出所有符合条件的串,同时可以求出满足条件最短子串。
用Java实现的代码如下:
public static void MinSubString(String src, String dest) {
int min = Integer.MAX_VALUE;// 找最短子串
int minfront = 0;// 最短子串开始位置
int minrear = 0;// 最短子串结束位置
int front, rear;
front = rear = 0;
int count = 0;
int hashtable[] = new int[256];
int cnt[] = new int[256];
for (int i = 0; i < dest.length(); i++) {
hashtable[dest.charAt(i)] = 1;
}
while (rear < src.length()) {
if (hashtable[src.charAt(rear)] == 1) {// rear当前字符在字符集中
// 判断是否是本子串中第一次检索到此字符,由count统计字符集中已出现的字符数
if (cnt[src.charAt(rear)] == 0) {
count++;
cnt[src.charAt(rear)]++;
if (count == dest.length()) {// 字符集中的字符在本子串中都已检索到
while (true) {
if (hashtable[src.charAt(front)] == 1) {// front当前字符在字符集中
cnt[src.charAt(front)]--;
// 字符集中某个字符为0,此时front到rear所指字符串即为符合条件的子串
if (cnt[src.charAt(front)] == 0) {
for (int i = front; i <= rear; i++) {
System.out.print(src.charAt(i));
}
System.out.println();
if (rear - front + 1 < min) {
min = rear - front + 1;
minrear = rear;
minfront = front;
}
// count不需要清空(赋0),cnt数组也不需要任何操作
count--;// 因为某个字符为出现次数已经减为0了,所以count--
front++;// 这个不可少
break;
}
}
front++;
}
}
} else {
cnt[src.charAt(rear)]++;
}
}
rear++;
}
if (min == Integer.MAX_VALUE) {
System.out.println("没有找到需要和谐的字符串");
} else {
System.out.println("最短字符串是:");
for (int i = minfront; i <= minrear; i++) {
System.out.print(src.charAt(i));
}
System.out.println();
}
}
用src="ab1dkj2ksjf3ae32ks1iji2sk1ksl1223ab;1ik3saj123",dest="123"。
测试了下,输入如下:
1dkj2ksjf3
32ks1
1223
23ab;1
3saj12
123
最短字符串是:
123