• 【百度面试题】求包括固定字符集的最短子串


    问题描写叙述:

    一串首尾相连的珠子(m个)。有N种颜色(N<=10),设计一个算法。取出当中一段,要求包括全部N中颜色,并使长度最短。

    并分析时间复杂度与空间复杂度。

    问题可等同于求一个长字符串c中包括固定字符集target的最短子串

    如"abddcbda"中包括"abc"的最短子串是"cbda"

    算法:

    指针head,rear分别指向眼下已知的最短子串。初始值为c的头和尾

    指针begain。end是当前检查的子串,一旦该串包括了所有target,就比較它和已知最短子串的长度。若它更短,设为新的最短子串

    数组targetlist记录target中每一个元素在begain-end段中出现的次数

    begain end初始值均为c的头,对begain end进行的操作例如以下:

    若end在target中,targetlist中对应元素(end出现的次数)加一,转②若end不在target中转④

    从begain開始检查。若begain出现的次数大于1或者begain不在target中。begain前移。继续检查。直到begain仅仅出现一次。

    这一步保证begain-end段是在不丢失target的情况下最短的转③

    检车begain-end段,一旦该段包括了所有target,就比較它和已知最短子串(head-rear段)的长度,若它更短。设为新的最短子串。begain++,转④

    end++转①


    这样仅仅需一次遍历。就能够找出最短子串,时间复杂度为O(n),n为c长度

    空间复杂度为O(n+m) ,m为字符集target中元素的个数


    代码实现:

    #pragma once
    #include<iostream>
    using namespace std;
    
    bool Intarget(char* end, char* target, int* clist, int* (&targetlist), int count)
    {
    	for (int i = 0; i < (int)strlen(target); i++)
    	{
    		if (*(target + i) == *end)
    		{
    			targetlist[i]++;//出现此事加1
    			clist[count] = i;//end指向的元素是target中第i个元素
    			return true;
    		}
    	}
    	return false;
    }
    
    //缩减begain-end段,保证它里面目标个数不变的情况下,该段最小
    void Reduce(char*(&c), char*(&target),char*(&begain), char*(&end), int*(&clist), int* (&targetlist))
    {
    	for (int i = begain - c; i <= end - c; i++)
    	{
    		if (targetlist[clist[i]] > 1)//begain指向的元素出现次数大于1,,begain前移
    		{
    			begain++;
    			targetlist[clist[i]]--;
    		}
    		else if (clist[i] == -1)//begain指向的元素不在target中,begain前移
    			begain++;
    		else
    			return;
    	}
    }
    
    //检查begain-end是否包括全部target,若是。且它长度小于head-rear段,将它设为新的head-rear
    void Checkall(char*(&c), char*(&target), int*targetlist, int* clist, char*(&begain), char*(&end), char*(&head), char*(&rear))
    {
    	bool all = true;
    	//检查begain-end这一段,target中的元素是否全都出现
    	for (int i = 0; i < (int)strlen(target); i++)
    		if (targetlist[i]>0)
    			continue;
    		else
    		{
    			all = false;
    			break;
    		}
    	//head-rear取小值
    	if (all&& end - begain < rear - head)
    	{
    		head = begain;
    		rear = end;
    		targetlist[clist[begain - c]]--;
    		begain++;
    	}
    }
    
    void Find(char* c, char* target, char* (&head), char* (&rear))
    {
    	int *targetlist = new int[strlen(target)];
    	for (int i = 0; i < (int)strlen(target); i++)//target中元素已出现的次数
    		targetlist[i] = 0;
    	int *clist = new int[strlen(c)];
    	for (int i = 0; i < (int)strlen(c); i++)//c中第i个元素在target中的位置
    		clist[i] = -1;
    
    	char *begain, *end;//当前正在检查的段
    	head = c;//指向头
    	rear = head + strlen(c)-1;//指向尾
    	begain = end = c;
    	while (*end)
    	{
    		if (Intarget(end, target, clist, targetlist, end - c))//假设*end在target中
    		{
    			//缩减begain-end段
    			Reduce(c, target, begain, end, clist, targetlist);
    			//检查begain-end段是否包括全部target。是,则比較长度,小了就生成新的head。rear
    			Checkall(c, target, targetlist,clist, begain, end, head, rear);
    		}
    		end++;
    	}
    }
    
    void  main()
    {
    	char* c = "abddcbdabcd";
    	char* target = "abc";
    	int *targetlist = new int[strlen(target)];
    	char *head = new char;
    	char* rear = new char;//保存最小段
    	Find(c, target, head, rear);
    	cout << "最短子串长度:" << rear - head +1<< endl;
    	cout << "最短子串:";
    	for (int i = 0; i <= rear - head; i++)
    		cout << *(head + i)<<' ';
    	cout << endl;
    	cout << "最短子串的head位置:" << head - c << endl;
    	cout << "最短子串的rear位置:" << rear - c << endl;
    	system("pause");
    }
    

    执行结果:



  • 相关阅读:
    使用ConfigFilter
    读取特定文件,替换第一行内容
    sqlserver,oracle,mysql等的driver驱动,url怎么写
    Excel 数字处理
    ResultMap详解
    正则表达式
    Tomasulo algorithm
    scoreboarding
    data hazard in CPU pipeline
    差分绕线间距对阻抗的影响
  • 原文地址:https://www.cnblogs.com/zsychanpin/p/6816539.html
Copyright © 2020-2023  润新知