数据结构实验复习

文章目录

  • 字符串的查找和删除
  • 后缀子串排序
  • 最短路径1
  • Disk-Tree
  • 火车出站

字符串的查找和删除

问题描述:
给定一个短字符串(不含空格),再给定若干字符串,在这些字符串中删除所含有的短字符串。
输入:
输入只有1组数据。
输入一个短字符串(不含空格),再输入若干字符串直到文件结束为止。
输出:
删除输入的短字符串(不区分大小写)并去掉空格,输出。
样例输入:
in
#include
int main()
{

printf(" Hi ");
}
样例输出:
#clude
tma()
{

prtf(“Hi”);
}
注:将字符串中的In,IN,iN,in删除

思路

  1. 本来的想法是利用string类处理字符串,结合find()erase()函数删除所有目标字符串,由于题目要求是不区分大小写,所以该想法难以实现,因为如果目标字符串长度为n,大小写组合有 2 n 2^n 2n种,时间复杂度很高。
int pos = ans.find(temp);
while (pos != ans.npos) //如果没找到,返回一个特别的标志,C++用npos表示
{
	ans.erase(pos, 2);
	pos = ans.find(temp);
}
  1. 思考后换了一个思路:将输入的字符转换为小写,将与目标字符串匹配的子串忽略,只将符合条件的输入字符存放到另一个字符数组中输出。
    这一题花了四百年。。?
    修改后代码如下:
#include 
#include 
#include 
using namespace std;
char temp[1000];
char c[1000];
char a[1000];
int main()
{
	cin >> temp;
	int len = strlen(temp);
	//将目标字符串统一为小写
	for (int i = 0; i < len; i++)
	{
		temp[i] = tolower(temp[i]);
	}
	int i = 0;
	int x = 0;
	//注意:cin.get()将缓冲区的‘\n’取走,否则c[0]='\n'
	cin.get();
	cin.getline(c, 10000, EOF);
	while (i<strlen(c))
	{
		//将统一为小写之前的字符串存在数组a中
		a[x] = c[i];
		c[i] = tolower(c[i]);
		if (c[i] == ' ') x--;
		int k;
		int j = i;
		for (k = len - 1; k >= 0; k--)
		{
			if (c[j] != temp[k])
				break;
			j--;
		}
		//注意:正常结束循环跳出时 k=-1 !!!
		if (k == -1)
			x = x - len;
		i++;
		x++;
	}
	cout << a;
	return 0;
}

有一点小疑问,为什么输出a会换行??

  1. 额,后来参考了别人AC的代码,用string类中的函数也是可以的,就是将第一个思路和第二个思路结合起来?。当时没有想到将字符串转换为小写,存到另一个字符串中,两个字符串同时调用erase()。
#include
#include
#include
using namespace std;
int main()
{
	string del, rep,lrep;
	int index, k;
	getline(cin, del);
	for (int i = 0; i < del.length(); i++) {
		del[i] = tolower(del[i]);
	}
	while (getline(cin, rep)) 
	{
		lrep = "";
		index = 0;
		for (int i = 0; i < rep.length(); i++)
			lrep += tolower(rep[i]);
		while (lrep.find(del, index) != string::npos)
		{
			k = lrep.find(del, index);
			lrep.erase(k, del.length());
			rep.erase(k, del.length());
			index = k;
		}
		index = 0;
		while (lrep.find(" ", index) != string::npos)
		{
			k = lrep.find(" ", index);
			lrep.erase(k, 1);
			rep.erase(k, 1);
			index = k;
		}
		cout << rep << endl;
	}
	return 0;
}

后缀子串排序

问题描述:
对于一个字符串,将其后缀子串进行排序,例如grain
其子串有:
grain
rain
ain
in
n
然后对各子串按字典顺序排序,即:
ain,grain,in,n,rain、
输入:
每个案例为一行字符串。
输出
将子串排序输出
样例输入:
grain
banana
样例输出:
ain
grain
in
n
rain
a
ana
anana
banana
na
nana
思路:用STL中的set容器,元素插入后是有序的,通过迭代器遍历就能得到子串的字典序排列。
代码如下:

#include
#include
#include
using namespace std;
set<string> st;
int main()
{
	string s;
	while(cin>>s)
	{
		st.clear();
		int len = s.length();
		int i = 1;
		st.insert(s);
		//每次删除字符串首的一个字符,得到一个子串
		//注意循环条件
		while(i<len)
		{
			s.erase(0, 1);
			st.insert(s);
			i++;
		}
		//只能通过迭代器遍历set
		for(set<string>::iterator it=st.begin();it!=st.end();it++)
    	{
        	cout<<*it<<endl;
 		}
	}
}

最短路径1

题目描述:
有n个城市m条道路(n<1000, m<10000),每条道路有个长度,请找到从起点s到终点t的最短距离和经过的城市名。
输入:
输入包含多组测试数据。
每组第一行输入四个数,分别为n,m,s,t。
接下来m行,每行三个数,分别为两个城市名和距离。
输出:
每组输出占两行。
第一行输出起点到终点的最短距离。
第二行输出最短路径上经过的城市名,如果有多条最短路径,输出字典序最小的那条。若不存在从起点到终点的路径,则输出“can’t arrive”。
样例输入:
3 3 1 3
1 3 3
1 2 1
2 3 1
样例输出:
2
1 2 3
思路:前几天复习了最短路径,因为此题要输出经过的城市,所以用采用dfs遍历,找出所有可达的路径,输出最短的那一条。

#include 
using namespace std;
int e[1000][1000];
int book[1000] = {0};//book标记某个城市是否走过
int flag[1000] = {0};//flag标记某个城市是否在可达路径中,最终得到最短可达路径经过的城市
int n, m;
int s, t;
int INF = INT32_MAX;
int Min = INF;
void dfs(int cur, int dis);
int main()
{
	int i, j;
	while (cin >> n >> m >> s >> t)
	{
		//注意将Min,book,flag初始化!
		Min = INF;
		for (int i = 1; i <= n; i++)
		{
			book[i] = 0;
			flag[i] = 0;
		}
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++)
			{
				if (i == j)
					e[i][j] = 0;
				else
					e[i][j] = INF;
			}
		for (int k = 0; k < m; k++)
		{
			cin >> i >> j;
			cin >> e[i][j];
		}
		book[s] = 1;
		dfs(s, 0);
		if (Min < INF)
			cout << Min << endl;
		else
			cout << "can't arrive" << endl;
		for (int i = 1; i <= n;i++)
		{
			if(flag[i]==1)
				cout << i<< " ";
		}
		cout << endl;
	}
	return 0;
}
void dfs(int cur, int dis)
{
	//剪枝
	if(dis>INF)
		return;
	if (cur == t)
	{
		//更新最短路径
		if(dis<Min)
		{
			Min = dis;
			for (int i = 1; i <= n;i++)
				flag[i] = book[i];
		}
		return;
	}
	else
	{
		for (int i = 1; i <= n;i++)
		{
			if(book[i]==0&&e[cur][i]>0&&e[cur][i]!=INF)
			{
				book[i] = 1;
				dfs(i, dis + e[cur][i]);
				book[i] = 0;
			}
		}
	}
}

后来发现该解法只能从1~n依次输出经过的点,并不能按序输出路径上的点,想当然了,考虑不全面……借鉴https://blog.csdn.net/jinixin/article/details/52247763的思路,得到以下代码。

#include 
using namespace std;
void dijsktra();
void Print(int x);
int e[1000][1000];
int n, m, s, t;
int dis[1000];
int INF = 999999999;
int book[1000] = {0};
int Min = INF;
int Path[1000];
int main()
{
	int i, j;
	while (cin >> n >> m >> s >> t)
	{
		Min = INF;
		for (int i = 1; i <= n; i++)
		{
			book[i] = 0;
			Path[i] = 0;
		}
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++)
			{
				if (i == j)
					e[i][j] = 0;
				else
					e[i][j] = INF;
			}
		for (int k = 0; k < m; k++)
		{
			cin >> i >> j;
			cin >> e[i][j];
		}
		for (int i = 1; i <= n; i++)
			dis[i] = e[s][i];
		book[s] = 1;
		dijsktra();
		if (dis[t] != INF)
		{
			cout << dis[t] << endl;
			Print(t);
		}
		else
			cout << "can't arrive";
		cout << endl;
	}
	return 0;
}
void dijsktra()
{
	int count = 1;
	while (count < n)
	{
		Min = INF;
		int j;
		for (int i = 1; i <= n; i++)
		{
			if (book[i] == 0 && dis[i] < Min && dis[i] != INF)
			{
				Min = dis[i];
				j = i;
			}
		}
		book[j] = 1;
		for (int i = 1; i <= n; i++)
			if (dis[i] > dis[j] + e[j][i])
			{
				dis[i] = dis[j] + e[j][i];
				Path[i] = j;
			}
		count++;
	}
}
void Print(int x)
{
	if (x == 0)
		cout << s << " ";
	else
	{
		Print(Path[x]);
		cout << x << " ";
	}
}

Disk-Tree

问题描述:
略,看输入输出可知~
样例输入:

7
WINNT\SYSTEM32\CONFIG
GAMES
WINNT\DRIVERS
HOME
WIN\SOFT
GAMES\DRIVERS
WINNT\SYSTEM32\CERTSRV\CERTCO~1\X86

样例输出:

GAMES
 DRIVERS
HOME
WIN
 SOFT
WINNT
 DRIVERS
 SYSTEM32
  CERTSRV
   CERTCO~1
    X86
  CONFIG

思路:以每个目录名作为字符建立一个字典树即可,每个节点的关系可以用map优化。其中m[now][str]=++tot,now编号相同的文件str即在同一个目录下。对m[now]用迭代器遍历,可得到当前目录文件的字典序排列。

#include
#include
#include
using namespace std;
string t, s;
map<string, int>M[20001];
int tot, n, p, pos;
void pre(int m, int s) { //m:当前编号,s:前导空格 
	map<string, int>::iterator it;
	for (it = M[m].begin(); it != M[m].end(); ++it) {
		for (int i = 0; i < s; i++) cout << ' ';
		cout << it->first << endl;
		pre(it->second, s + 1);
	}
}
int main()
{
	cin >> n;
	while (n--) {
		int now = 0;   //当前所在树的结点编号 
		cin >> t;
		pos = p = 0;     //将'\'作为分隔符依次抽取子串插入树中 
		while ((p = t.find('\\', p)) != -1) {
			s = t.substr(pos, p - pos);
			if (!M[now].count(s)) M[now][s] = ++tot;
			now = M[now][s];
			pos = ++p;
		}
		s = t.substr(pos);
		if (!M[now].count(s)) M[now][s] = ++tot; //处理最后一个文件名 
	}
	pre(0, 0);
	return 0;
}

火车出站

题目描述:
铁路进行列车调度时,常把站台设计成栈式结构的站台,试问:
设有编号为1到n的n辆列车,顺序开入栈式结构的站台,则可能的出栈序列有多少种?
输入
输入包含多组测试数据。每组为一个正整数n(1<=n<=20),表示有n辆列车。
输出
输出可能的出栈序列有多少种。
样例输入
4
3
样例输出
14
5
思路:

  1. 先对1-n进行全排列,然后判断是否是合法的出站序列。用栈来模拟站台,数组B存放待判断的序列,A取值1-n,表示当前来的列车编号。每一辆列车进站都有三种情况:
  • 不进站,直接开走
  • 进站之后在下一辆列车来之前出站
  • 进站
		if (A == a[B])  //列车A直接开走,不进站
		{
			A++;
			B++;
		}
		else if (!s.empty() && a[B] == s.top()) //栈不空,并且栈顶元素与目标序列相等,a[B]出站
		{
			s.pop();
			B++;
		}
		else if (A <= n)    //A进站
		{
			s.push(A); A++;
		}
		else
		{
			flag = 1;
			break;
		}

总的代码如下:

#include
#include
using namespace std;
int a[1000];
bool Judge();
void Perm(int k);
int n;
int Count = 0;
int main()
{
	while (cin >> n)
	{
		Count = 0;
		for (int i = 0; i < n; i++)
			a[i] = i + 1;
		Perm(0);
		cout << Count << endl;
	}
	return 0;
}
void Perm(int k)
//递归产生前缀是a[0:k-1],且后缀是a[k:n]的全排列的所有排列
{
	if (k == (n - 1))
	{
		if (Judge())
			Count++;
	}
	else
	{
		for (int i = k; i < n; i++)
		{
			int temp = a[k]; a[k] = a[i]; a[i] = temp;
			Perm(k + 1);
			temp = a[k]; a[k] = a[i]; a[i] = temp;
		}
	}
}
bool Judge()
//判断是不是正确的出站序列
{
	int A = 1, B = 0;//A是原序列(1-n),B是排序后数组下标
	int flag = 0;
	stack<int> s; //站台
	while (B < n)
	{
		if (A == a[B])  //列车A直接开走,不进站
		{
			A++;
			B++;
		}
		else if (!s.empty() && a[B] == s.top()) //栈不空,并且栈顶元素与目标序列相等,a[B]出站
		{
			s.pop();
			B++;
		}
		else if (A <= n)    //A进站
		{
			s.push(A); A++;
		}
		else
		{
			flag = 1;
			break;
		}
	}
	if (flag == 0)
		return true;
	else return false;
}
  1. 还有一种方法就是直接带公式。
#include 
using namespace std;
int main()
{
	int n;
	long long t[21];
	t[0] = 1;
	for (int i = 1; i <= 20; i++)
		{
			t[i] = t[i - 1] * (4 * i - 2) / (i + 1);
		}
	while (cin >> n)
	{
		cout << t[n] << endl;
	}
	return 0;
}

你可能感兴趣的:(算法)