[数据结构]算法设计题--链表后缀共同空间

51 篇文章 2 订阅
29 篇文章 8 订阅

问题

采用带头结点的单链表保存单词,当两个单词有相同的后缀时,则可以共享相同的后缀存储空间。例如,“loading”和“being”的存储空间影像如图。
在这里插入图片描述

设str1和str2分别指向两个单词所在链表的头结点,链表结点结构为(data,next),请设计一个时间上尽可能高效的算法,找出由str1和str2所指的两个链表共同
后缀的起始位置。要求:

  1. 给出算法的基本设计思想
  2. 说明算法的时间复杂度。

解答

算法思想

因为两个链表长度不一定相同,所以从头开始同时遍历到尾结点,并不能保证同时到达。假设一个链表比另一个长k个结点,将长链表先移动k个位置,然后再与短链表同时开始遍历,检查指针是否指向同一个结点,如果是则为共同后缀的起始位置。

算法分析

时间复杂度为O(m+n),空间复杂度为O(1)。

/*
采用带头结点的单链表保存单词,当两个单词有相同的后缀时,则可以共享相同的后缀存储空间。例如,“loading”和“being”的存储空间影像如图。
设str1和str2分别指向两个单词所在链表的头结点,链表结点结构为(data,next),请设计一个时间上尽可能高效的算法,找出由str1和str2所指的两个链表共同
后缀的起始位置。要求:
1. 给出算法的基本设计思想
2. 说明算法的时间复杂度。
*/
#include <iostream>
#include <string>
using namespace std;
typedef struct LNode {
	char data;
	struct LNode *next;
}LNode, *LinkList;

void createList(LinkList &L, string s) {
	L = new LNode;
	L->next = NULL;
	LinkList pc = L, p;
	for (int i = 0; i < s.size(); ++i) {
		p = new LNode;
		p->data = s[i];
		p->next = pc->next;
		pc->next = p;
		pc = p;
	}
}

void printList(LinkList L) {
	LinkList p = L->next;
	while (p) {
		cout << p->data;
		p = p->next;
	}
	cout << endl;
}

LinkList findSuffix(LinkList L1, LinkList L2) {
	LinkList p1 = L1->next; // p1指向str1的首元结点
	LinkList p2 = L2->next; // p2指向str2的首元结点

	// lne1为str1的长度,len2为str2的长度
	LinkList tmp = p1;
	int len1 = 0, len2 = 0;
	while (tmp) { 
		tmp = tmp->next;
		len1++;
	}
	tmp = p2;
	while (tmp) {
		tmp = tmp->next;
		len2++;
	}

	// 计算表长之差,找出长str对应的链表
	int advance = 0;
	LinkList longPoint, shortPoint;
	if (len1 > len2) {
		longPoint = p1;
		shortPoint = p2;
		advance = len1 - len2;
	} else {
		longPoint = p2;
		shortPoint = p1;
		advance = len2 - len1;
	}

	// 长链表上遍历advance个结点
	while (advance--) {
		longPoint = longPoint->next;
	}

	// 同步遍历两个链表,寻找共用存储空间的位置
	while (longPoint) {
		if (longPoint == shortPoint) {
			return longPoint;
		} else {
			longPoint = longPoint->next;
			shortPoint = shortPoint->next;
		}
	}
	// 查找失败
	return NULL; 
}

int hasSuffix(string s1, string s2) {
	int len1 = s1.size() - 1;
	int len2 = s2.size() - 1;
	int ans = 0;
	while (len1 >= 0 && len2 >= 0) {
		if (s1[len1] == s2[len2]) {
			ans++;
			len1--;
			len2--;
		} else {
			break;
		}
	}
	if (ans > 0) {
		return len1 + 1;
	} else {
		return -1;
	}
}

int main() {
	LinkList L1 = NULL, L2 = NULL;
	string s1, s2;
	cin >> s1 >> s2;
	int len1 = s1.size(), len2 = s2.size();

	// 判断共同后缀空间位置
	int position = hasSuffix(s1, s2);
	if (position == -1) {
		cout << "无共享后缀空间" << endl;
	} else {
		// 创建不同的前部子串
		createList(L1, s1.substr(0, position));
		createList(L2, s2.substr(0, position + len2 - len1));

		// 构造共同后缀链表
		LinkList head = new LNode;
		head->next = NULL;
		LinkList pc = head;
		LinkList p;
		for (int i = position; i < s1.size(); ++i) {
			p = new LNode;
			p->data = s1[i];
			p->next = pc->next;
			pc->next = p;
			pc = p;
		}

		// 接在L1和L2的后面
		LinkList p1 = L1;
		while (p1->next) {
			p1 = p1->next;
		}
		LinkList p2 = L2;
		while (p2->next) {
			p2 = p2->next;
		}
		p1->next = head->next;
		p2->next = head->next;
		// printList(L1);
		// printList(L2);
		// 查找后缀开始的结点
		LinkList findNode = findSuffix(L1, L2);
		if (findNode == NULL) {
			cout << "无共享后缀空间" << endl;
		} else {
			cout << "位置元素:" << findNode->data << endl;
		}
	}
	return 0;
}

  • 5
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值