//给定长度为m的短字符串s1,以及一个长度为n的长字符串str,m<n
//问能否在str中找到一个长度为m的连续子串使得这个子串刚好由s1的m个字符组成,顺序无所谓,返回任意满足条件的一个子串的起始位置
第一种思路,比较常规的,时间复杂度是O(n*m), 先给子串中的每个字符构建一个hash表, 然后给str中长度为m的子串也构建一个hash表, 比较2个hash是否相同,如果相同,那就是同源异构子串.
- (void)test1 {
//给定长度为m的字符串s1,以及一个长度为n的字符串str
//问能否在str中找到一个长度为m的连续子串使得这个子串刚好由s1的m个字符组成,顺序无所谓,返回任意满足条件的一个子串的起始位置,未找到返回-1
NSString * str = @"acdabcaafg";
NSString * s1 = @"abac";
NSDictionary<NSString*,NSNumber*> * s1Dic = [self getDictionaryWithStr:s1];
for (int i=0; i<str.length-s1.length; i++) {
NSString * temp = [str substringWithRange:NSMakeRange(i, s1.length)];
NSDictionary * tempDic = [self getDictionaryWithStr:temp];
if ([tempDic isEqual:s1Dic]) {
NSLog(@"找到了 %d %@",i,temp);
}
}
}
// 把字符串中的每个字符串截取成一个数组
- (NSArray <NSString *> *)subStr:(NSString *)str {
NSMutableArray * array = [NSMutableArray array];
for (int i = 0; i<str.length; i++) {
NSString * oneStr = [str substringWithRange:NSMakeRange(i, 1)];
[array addObject:oneStr];
}
return array;
}
/// 构建每个字符组成的字典,统计每个字符出现的次数
- (NSMutableDictionary *)getDictionaryWithStr:(NSString *)str {
NSArray <NSString *> * array = [self subStr:str];
NSMutableDictionary * dic = [NSMutableDictionary dictionary];
for (NSString * key in array) {
if (dic[key]) {
dic[key] = @([dic[key] intValue] + 1);
continue;
}
dic[key] = @(1);
}
return dic;
}
网上的比较优秀的一个, 对这个比较是否相同做了优化, 首先还是子串构造出一个hash表,但是不是通过2个hash表是否相同,而是用了一个wrongNum,记录不符合的字符数量,如果不符合的字符数量超过0,那就不满足条件,如果为0,那就是同源异构子串.
用子串构造出的hash表, 然后str生成一个大小为m的窗口, str中的窗口来填这个hash表,如果填到所有值为0,那就是同源异构子串.
比上一个算法, 好处就是时间复杂度为O(n), 但是有了这个wrongNum,维护起来,理解起来有点难了.
- (void)test2{
//给定长度为m的字符串s1,以及一个长度为n的字符串str
//问能否在str中找到一个长度为m的连续子串使得这个子串刚好由s1的m个字符组成,顺序无所谓,返回任意满足条件的一个子串的起始位置,未找到返回-1
NSString * str = @"acdabcaafg";
NSString * s1 = @"acd";
NSMutableDictionary<NSString*,NSNumber*> * s1Dic = [self getDictionaryWithStr:s1];
int wrongNum = 0; // 记录s1Dic中的错误数量
int right = 0; // 窗口右侧值
NSArray * strArray = [self subStr:str];
// 构造第一个窗口
for (; right<s1.length; right++) {
// 依次把字母加进去,如果这个字母没有在s1Dic中出现,说明是多添加的,比如f,那么需要把字典中的错误数量加1
if ([s1Dic[ strArray[right] ] intValue] <= 0){
wrongNum ++;
}
s1Dic[ strArray[right] ] = @([s1Dic[ strArray[right] ] intValue]-1);
}
for (; right<str.length; right++) {
if (wrongNum == 0) {
NSLog(@"找到了 就是 %lu",right-s1.length);
}
// 把下一个字母加到窗口中去,如果为0,需更新错误数量
if ([s1Dic[ strArray[right] ] intValue] <= 0){
wrongNum ++;
}
s1Dic[ strArray[right] ] = @([s1Dic[ strArray[right] ] intValue]-1);
// 把窗口最左边的字母去掉,如果为0,减少错误数量
if ([s1Dic[ strArray[right-s1.length] ] intValue] < 0){
wrongNum --;
}
s1Dic[ strArray[right-s1.length] ]= @([s1Dic[ strArray[right-s1.length] ] intValue]+1);
}
// 最后一个结束的判断
if (wrongNum == 0) {
NSLog(@"找到了 就是 %lu",right-s1.length);
}
NSLog(@"寻找结束");
}
// 把字符串中的每个字符串截取成一个数组
- (NSArray <NSString *> *)subStr:(NSString *)str {
NSMutableArray * array = [NSMutableArray array];
for (int i = 0; i<str.length; i++) {
NSString * oneStr = [str substringWithRange:NSMakeRange(i, 1)];
[array addObject:oneStr];
}
return array;
}
/// 构建每个字符组成的字典,统计每个字符出现的次数
- (NSMutableDictionary *)getDictionaryWithStr:(NSString *)str {
NSArray <NSString *> * array = [self subStr:str];
NSMutableDictionary * dic = [NSMutableDictionary dictionary];
for (NSString * key in array) {
if (dic[key]) {
dic[key] = @([dic[key] intValue] + 1);
continue;
}
dic[key] = @(1);
}
return dic;
}
如果2个字符串的字符一样,无论顺序是否一致,都可以认为是兄弟字符串,比如"abcc" , "acbc" 就是兄弟字符串 , 写出一个算法,判断是否是兄弟字符串.
生成一个hash表统计每个字符出现的次数,然后比较hash表的内容是否一致,如果一致,就说明是兄弟字符串.
//测试用例
// int result = isBrother("abcc", "ac");
int result = isBrother("wwwbaiducom", "combaiduwww");
// int result = isBrother("abcc", "acbcsda");
// int result = isBrother("abcc", "ccbb");
// int result = isBrother("abcc", "acbd");
// int result = isBrother("abcc", "acbc");
NSLog(@"%d",result);
int isBrother(char * str1,char * str2) {
// 2个桶保存每次字符出现的次数
int arrayA[26] ={0};
int arrayB[26] ={0};
// 生成第一个桶, 字符-'a'就是桶的下标
for (int i = 0; str1[i]!='\0'; i++) {
arrayA[str1[i]-'a'] ++;
}
for (int i = 0; str2[i]!='\0'; i++) {
arrayB[str2[i]-'a'] ++;
}
// 比较桶内元素的数量
for (int i = 0; i<26; i++) {
if (arrayA[i] == arrayB[i]) {
continue;
}
return 0;
}
return 1;
}