07--BF RK算法
[toc]
去除重复字⺟
给你⼀个仅包含⼩写字⺟的字符串,请你去除字符串中重复的字⺟,使得每个字⺟只出现⼀ 次。需保证返回结果的字典序最⼩(要求不能打乱其他字符的相对位置)
解题思路:
-
判断字符串可能出现的特殊情况
-
用一个
record数组
记录字符串中字母
出现的次数; -
申请一个字符串栈
stack
用来存储去除重复字母的结果,并利用它的特性帮助我们找到正确的次序; -
遍历字符串
s
-
从
0~top
,遍历stack
判断当前字符s[i]
是否存在于栈stack
中
如果当前字符是否存在于栈的定义一个falg
标记isExist
,0
表示不存在,1
表示存在 -
如果
isExist
存在,record[s[i]]
位置上的出现次数减一,并继续遍历下一个字符; 表示当前的stack已经有这个字符了没有必要处理这个重复的字母; -
如果
isExist
不存在,则
如果不存在,则需要循环一个找到一个正确的位置,然后在存储起来;
如果不存在,跳过栈中所有比当前字符大、且后面还会出现的元素,然后将当前字符入栈
top > -1
表示栈非空
stack[top] > s[i]
表示栈顶元素比当前元素大
record[stack[top]] > 1
表示后面还会出现
通过一个while
循环找到将栈中位置错误的数据,出栈. 找当前合适的位置,则结束while
循环;
找到合理的位置后,则将当前字符s[i]
入栈; -
直到遍历完所有字符后,则为字符串栈
stack
添加一个结束符'\0'
,并返回当前字符串首地址;
char *removeDuplicateLetters(char *s){
//为空 长度为0直接返回
if(*s == NULL || strlen(s) == 0){
return "";
}
//只有一个直接返回
if (strlen(s) == 1){
return *s;
}
int record[26] = {0}; //存放对应26字母出现的次数
int len = strlen(s);
char *stack = (char *)malloc(sizeof(char)*len); //开辟空间
memset(stack, 0, len); //赋值0
//记录
for (int i=0; i<len; i++) {
record[s[i]-'a']++;
}
int top=-1;
int isExist=0;
for (int i=0; i<len; i++) {
//存在 退出循环
for (int j=0; j<=top; j++) {
if (s[i] == stack[j]){
isExist =1;
break;
}
}
if (isExist == 1){
//已经有了 --
record[s[i]-'a']--;
}else{
// 栈有值,栈顶元素大于s[i],且栈顶元素后面还有
while (top>-1 && stack[top] > s[i] && record[stack[top] -'a']>1) {
//记录次数--
record[stack[top] -'a']--;
//出栈
top--;
}
}
//将s[i]存入
top++;
stack[top]=s[I];
}
stack[++top] ='\0';
return stack;
}
字符串查找
- 题目: 有一个主串
S = {a, b, c, a, c, a, b, d, c}
, 模式串T = { a, b, d }
; 请找到模
式串在主串中第一次出现的位置;
提示: 不需要考虑字符串大小写问题, 字符均为小写字母
BF
-
BF
算法-爆发匹配算法
将目标串S
的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果。BF算法是一种蛮力算法。
解题思路:
从
0
开始到len-
模式串的长度
外圈循环次数是strlen(S)-len
内圈循环模式串
判断S[i+j]!=T[j]
是否相等,不相等,i+1,从下一个开始
和模式串匹配,如果最后j=len-1
;说明匹配到了
int returnFirstAreaStr(char *S,char *T){
unsigned int len = strlen(T);
for(int i=0;i<(strlen(S)-len);i++){
for(int j=0;j<len;j++){
if (S[i+j]!=T[j]){
break;
}
if (j == len-1){
return (i+1);
}
}
}
return -1;
}
RK
HASH!
如果两个字符串hash
后的值不相同,则它们肯定不相同;如果它们hash
后的值相同,它们不一定相同。
RK算法的基本思想就是:将模式串P
的hash
值跟主串S中的每一个长度为|P|
的子串的hash值比较。如果不同,则它们肯定不相等;如果相同,则再诸位比较之。
如何将模式串或者主串拆分后的子串换算成一个哈希值?
在十进制下,123
和 456
的比较, 能不能将abc
和edf
的比较,计算成十进制下比较一样?
- 可以将字母转换成进制,在
ASCII
中,小写字母a
是65
,'b'-'a' = 1
; - 为了避免冲突,可以构造一个26进制,来记录每个字母对应的数组,
假设 模式串
4cfb3990a2ee3d38f82571a7f32708a9ccc
主串dbcedb
在26
进制下
aba65d9bdfc6355dca617a6ad893a03e
串哈希值求解规律
- 相邻的
2
个子串s[i]
与s[i+1]
(i表示子串从主串中的起始位置,子串的长度
都为m). 对应的哈希值计算公式有交集. 也就说我们可以使用s[i-1]
计算出s[i]
的哈希值;
9c1be5993f230a789c546ea86314e03d -
s[i+1]
实现上是上一个s[i]
去掉最高位数据,其余的m-1
为字符乘以
d
进制. 再加上最后一个为字符得到;
主串分割后的哈希值:St[i+1] = (st[i] - d2 ✖ s[i] ) ✖ d + s[i+m]
注意点,虽然哈希值也有存在不相同情况,如果查找到,在判断一下字符是否一样
//为了杜绝哈希冲突. 当前发现模式串和子串的HashValue 是一样的时候.还是需要二次确认2个字符串是否相等.
int isMatch(char *S, int i, char *P, int m)
{
int is, ip;
for(is=i, ip=0; is != m && ip != m; is++, ip++)
if(S[is] != P[ip])
return 0;
return 1;
}
- 上代码
#define d 26 //当前的进制
//4.为了杜绝哈希冲突. 当前发现模式串和子串的HashValue 是一样的时候.还是需要二次确认2个字符串是否相等.
int isMatch(char *S, int i, char *P, int m)
{
int is, ip;
for(is=i, ip=0; is != m && ip != m; is++, ip++)
if(S[is] != P[ip])
return 0;
return 1;
}
int getPower(int m){
int i = 0;
int n =1;
while (i < (m-1)) {
n = n*d;
I++;
}
return n;
}
int RK(char *S, char *P){
unsigned int n = (int)strlen(S); //主串长度
unsigned int m = (int)strlen(P); //模式串长度
unsigned int Pn = 0; //记录模式串哈希值
unsigned int St = 0; //记录主串哈希值
//123 == 0 + 1
// i=0 0 + 1 = 1
// i=1 10+2 = 12
// i=2 12*10 + 3 = 123
for (int i=0; i<m; i++) {
Pn = Pn * d + P[i]-'a';
St = St * d + S[i]-'a';
}
int constPower = getPower(m);
for (int j=0; j<n-m; j++) {
if (Pn == St && isMatch(S,j,P,m))
return j+1;
St = ((St - constPower * (S[j]-'a')) * d + (S[j+m]-'a'));
}
return -1;
}
打印
char *buf="abcabhgsabcabxhsjhgsdjsdhsdjsdh";
char *ptrn="hgs";
printf("主串为%s\n",buf);
printf("子串为%s\n",ptrn);
int index = RK(buf, ptrn);
printf("find index : %d\n",index);
主串为abcabhgsabcabxhsjhgsdjsdhsdjsdh
子串为hgs
find index : 6